From b9841f9cfa264cba52ba30f04b179bae10704b60 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 7 Feb 2020 10:47:58 -0500 Subject: [PATCH] a mess --- .../annotations/impl/EntityProcess.java | 86 ++++-- .../util/AnnotationProxyMaker.java | 287 ++++++++++++++++++ .../mindustry/annotations/util/Selement.java | 16 +- build.gradle | 3 + .../mindustry/entities/def/AllEntities.java | 23 +- 5 files changed, 369 insertions(+), 46 deletions(-) create mode 100644 annotations/src/main/java/mindustry/annotations/util/AnnotationProxyMaker.java diff --git a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java index 85c31e3184..f0bbc3619f 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java @@ -32,22 +32,46 @@ public class EntityProcess extends BaseProcessor{ ObjectMap> componentDependencies = new ObjectMap<>(); ObjectMap> defComponents = new ObjectMap<>(); ObjectMap varInitializers = new ObjectMap<>(); + ObjectMap methodBlocks = new ObjectMap<>(); ObjectSet imports = new ObjectSet<>(); + Array allGroups = new Array<>(); + Array allDefs = new Array<>(); + Array allInterfaces = new Array<>(); { - rounds = 2; + rounds = 3; } @Override public void process(RoundEnvironment env) throws Exception{ + allGroups.addAll(methods(GroupDef.class)); + allDefs.addAll(elements(EntityDef.class)); + allInterfaces.addAll(types(EntityInterface.class)); - //round 1: get component classes and generate interfaces for them + //round 1: generate component interfaces if(round == 1){ baseComponents = types(BaseComponent.class); - Array allGroups = methods(GroupDef.class); - Array allDefs = elements(EntityDef.class); Array allComponents = types(Component.class); + //store code + for(Stype component : allComponents){ + for(Svar f : component.fields()){ + VariableTree tree = f.tree(); + + //add initializer if it exists + if(tree.getInitializer() != null){ + String init = tree.getInitializer().toString(); + varInitializers.put(f, init); + } + } + + for(Smethod elem : component.methods()){ + if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue; + //get all statements in the method, store them + methodBlocks.put(elem, elem.tree().getBody().toString()); + } + } + //store components for(Stype type : allComponents){ componentNames.put(type.name(), type); @@ -58,13 +82,6 @@ public class EntityProcess extends BaseProcessor{ imports.addAll(getImports(comp.e)); } - //parse groups - for(Smethod group : allGroups){ - GroupDef an = group.annotation(GroupDef.class); - Array types = types(an, GroupDef::value); - groupDefs.add(new GroupDefinition(group.name(), ClassName.bestGuess(packageName + "." + interfaceName(types.first())), types, an.spatial(), an.mapping())); - } - //create component interfaces for(Stype component : allComponents){ TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)) @@ -112,11 +129,11 @@ public class EntityProcess extends BaseProcessor{ //setter if(!field.is(Modifier.FINAL) && !signatures.contains(cname + "(" + field.mirror().toString() + ")") && - !field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){ - inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC) - .addParameter(ParameterSpec.builder(field.tname(), field.name()) - .addAnnotations(Array.with(field.annotations()) - .select(a -> a.toString().contains("Null")).map(AnnotationSpec::get)).build()).build()); + !field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){ + inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC) + .addParameter(ParameterSpec.builder(field.tname(), field.name()) + .addAnnotations(Array.with(field.annotations()) + .select(a -> a.toString().contains("Null")).map(AnnotationSpec::get)).build()).build()); } } @@ -147,12 +164,23 @@ public class EntityProcess extends BaseProcessor{ .addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); inter.addMethod(MethodSpec.methodBuilder("draw" + Strings.capitalize(layer.name())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build()); write(inter); + } + }else if(round == 2){ //round 2: get component classes and generate interfaces for them + //parse groups + for(Smethod group : allGroups){ + GroupDef an = group.annotation(GroupDef.class); + Array types = types(an, GroupDef::value).map(this::interfaceToComp);; + groupDefs.add(new GroupDefinition(group.name(), ClassName.bestGuess(packageName + "." + interfaceName(types.first())), types, an.spatial(), an.mapping())); + } + + //add special generated groups + for(DrawLayer layer : DrawLayer.values()){ + String name = "DrawLayer" + Strings.capitalize(layer.name()) + "c"; //create group definition with no components directly GroupDefinition def = new GroupDefinition(layer.name(), ClassName.bestGuess(packageName + "." + name), Array.with(), false, false); //add manual inclusions of entities to be added to this group def.manualInclusions.addAll(allDefs.select(s -> allComponents(s).contains(comp -> comp.interfaces().contains(in -> in.name().equals(name))))); - groupDefs.add(def); } @@ -179,7 +207,6 @@ public class EntityProcess extends BaseProcessor{ //write fields to the class; ignoring transient ones Array fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT)); for(Svar f : fields){ - VariableTree tree = f.tree(); FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name()); //keep statics/finals if(f.is(Modifier.STATIC)){ @@ -187,10 +214,8 @@ public class EntityProcess extends BaseProcessor{ if(f.is(Modifier.FINAL)) fbuilder.addModifiers(Modifier.FINAL); } //add initializer if it exists - if(tree.getInitializer() != null){ - String init = tree.getInitializer().toString(); - varInitializers.put(f, init); - fbuilder.initializer(init); + if(varInitializers.containsKey(f)){ + fbuilder.initializer(varInitializers.get(f)); } if(!isFinal) fbuilder.addModifiers(Modifier.PROTECTED); @@ -235,12 +260,10 @@ public class EntityProcess extends BaseProcessor{ } for(Smethod elem : entry.value){ - if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue; + if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE) || !methodBlocks.containsKey(elem)) continue; //get all statements in the method, copy them over - MethodTree methodTree = elem.tree(); - BlockTree blockTree = methodTree.getBody(); - String str = blockTree.toString(); + String str = methodBlocks.get(elem); //name for code blocks in the methods String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", ""); @@ -387,8 +410,7 @@ public class EntityProcess extends BaseProcessor{ write(idBuilder); }else{ - //round 2: generate actual classes and implement interfaces - Array interfaces = types(EntityInterface.class); + //round 3: generate actual classes and implement interfaces //implement each definition for(EntityDefinition def : definitions){ @@ -397,7 +419,7 @@ public class EntityProcess extends BaseProcessor{ for(Stype comp : def.components){ //implement the interface - Stype inter = interfaces.find(i -> i.name().equals(interfaceName(comp))); + Stype inter = allInterfaces.find(i -> i.name().equals(interfaceName(comp))); if(inter == null){ err("Failed to generate interface for", comp); return; @@ -431,7 +453,7 @@ public class EntityProcess extends BaseProcessor{ TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL); //create mock types of all components - for(Stype interf : interfaces){ + for(Stype interf : allInterfaces){ //indirect interfaces to implement methods for Array dependencies = interf.allInterfaces().and(interf); Array methods = dependencies.flatMap(Stype::methods); @@ -502,7 +524,7 @@ public class EntityProcess extends BaseProcessor{ Array allComponents(Selement type){ if(!defComponents.containsKey(type)){ //get base defs - Array components = types(type.annotation(EntityDef.class), EntityDef::value); + Array components = types(type.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp); ObjectSet out = new ObjectSet<>(); for(Stype comp : components){ //get dependencies for each def, add them @@ -553,7 +575,7 @@ public class EntityProcess extends BaseProcessor{ } String createName(Selement elem){ - Array comps = types(elem.annotation(EntityDef.class), EntityDef::value); + Array comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);; comps.sortComparing(Selement::name); return comps.toString("", s -> s.name().replace("Comp", "")) + "Entity"; } diff --git a/annotations/src/main/java/mindustry/annotations/util/AnnotationProxyMaker.java b/annotations/src/main/java/mindustry/annotations/util/AnnotationProxyMaker.java new file mode 100644 index 0000000000..352c684e74 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/util/AnnotationProxyMaker.java @@ -0,0 +1,287 @@ +package mindustry.annotations.util; + +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.code.Attribute.Array; +import com.sun.tools.javac.code.Attribute.Enum; +import com.sun.tools.javac.code.Attribute.Error; +import com.sun.tools.javac.code.Attribute.Visitor; +import com.sun.tools.javac.code.Attribute.*; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.code.Type.ArrayType; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.*; +import sun.reflect.annotation.*; + +import javax.lang.model.type.*; +import java.io.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; +import java.util.Map.*; +import java.lang.Class; + +//replaces the standard Java AnnotationProxyMaker with one that doesn't crash +//thanks, oracle. +@SuppressWarnings({"sunapi", "unchecked"}) +public class AnnotationProxyMaker{ + private final Compound anno; + private final Class annoType; + + private AnnotationProxyMaker(Compound var1, Class var2){ + this.anno = var1; + this.annoType = var2; + } + + public static A generateAnnotation(Compound var0, Class var1){ + AnnotationProxyMaker var2 = new AnnotationProxyMaker(var0, var1); + return (A)var1.cast(var2.generateAnnotation()); + } + + private Annotation generateAnnotation(){ + return AnnotationParser.annotationForMap(this.annoType, this.getAllReflectedValues()); + } + + private Map getAllReflectedValues(){ + LinkedHashMap var1 = new LinkedHashMap(); + Iterator var2 = this.getAllValues().entrySet().iterator(); + + while(var2.hasNext()){ + Entry var3 = (Entry)var2.next(); + MethodSymbol var4 = (MethodSymbol)var3.getKey(); + Object var5 = this.generateValue(var4, (Attribute)var3.getValue()); + if(var5 != null){ + var1.put(var4.name.toString(), var5); + } + } + + return var1; + } + + private Map getAllValues(){ + LinkedHashMap var1 = new LinkedHashMap(); + ClassSymbol var2 = (ClassSymbol)this.anno.type.tsym; + + for(com.sun.tools.javac.code.Scope.Entry var3 = var2.members().elems; var3 != null; var3 = var3.sibling){ + if(var3.sym.kind == 16){ + MethodSymbol var4 = (MethodSymbol)var3.sym; + Attribute var5 = var4.getDefaultValue(); + if(var5 != null){ + var1.put(var4, var5); + } + } + } + + Iterator var6 = this.anno.values.iterator(); + + while(var6.hasNext()){ + Pair var7 = (Pair)var6.next(); + var1.put(var7.fst, var7.snd); + } + + return var1; + } + + private Object generateValue(MethodSymbol var1, Attribute var2){ + AnnotationProxyMaker.ValueVisitor var3 = new AnnotationProxyMaker.ValueVisitor(var1); + return var3.getValue(var2); + } + + private static final class MirroredTypesExceptionProxy extends ExceptionProxy{ + static final long serialVersionUID = 269L; + private transient List types; + private final String typeStrings; + + MirroredTypesExceptionProxy(List var1){ + this.types = var1; + this.typeStrings = var1.toString(); + } + + public String toString(){ + return this.typeStrings; + } + + public int hashCode(){ + return (this.types != null ? this.types : this.typeStrings).hashCode(); + } + + public boolean equals(Object var1){ + return this.types != null && var1 instanceof AnnotationProxyMaker.MirroredTypesExceptionProxy && this.types.equals(((AnnotationProxyMaker.MirroredTypesExceptionProxy)var1).types); + } + + protected RuntimeException generateException(){ + return new MirroredTypesException(this.types); + } + + private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException{ + var1.defaultReadObject(); + this.types = null; + } + } + + private static final class MirroredTypeExceptionProxy extends ExceptionProxy{ + static final long serialVersionUID = 269L; + private transient TypeMirror type; + private final String typeString; + + MirroredTypeExceptionProxy(TypeMirror var1){ + this.type = var1; + this.typeString = var1.toString(); + } + + public String toString(){ + return this.typeString; + } + + public int hashCode(){ + return (this.type != null ? this.type : this.typeString).hashCode(); + } + + public boolean equals(Object var1){ + return this.type != null && var1 instanceof AnnotationProxyMaker.MirroredTypeExceptionProxy && this.type.equals(((AnnotationProxyMaker.MirroredTypeExceptionProxy)var1).type); + } + + protected RuntimeException generateException(){ + return new MirroredTypeException(this.type); + } + + private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException{ + var1.defaultReadObject(); + this.type = null; + } + } + + private class ValueVisitor implements Visitor{ + private MethodSymbol meth; + private Class returnClass; + private Object value; + + ValueVisitor(MethodSymbol var2){ + this.meth = var2; + } + + Object getValue(Attribute var1){ + Method var2; + try{ + var2 = AnnotationProxyMaker.this.annoType.getMethod(this.meth.name.toString()); + }catch(NoSuchMethodException var4){ + return null; + } + + this.returnClass = var2.getReturnType(); + var1.accept(this); + if(!(this.value instanceof ExceptionProxy) && !AnnotationType.invocationHandlerReturnType(this.returnClass).isInstance(this.value)){ + this.typeMismatch(var2, var1); + } + + return this.value; + } + + public void visitConstant(Constant var1){ + this.value = var1.getValue(); + } + + public void visitClass(com.sun.tools.javac.code.Attribute.Class var1){ + this.value = new AnnotationProxyMaker.MirroredTypeExceptionProxy(var1.classType); + } + + public void visitArray(Array var1){ + Name var2 = ((ArrayType)var1.type).elemtype.tsym.getQualifiedName(); + int var6; + if(var2.equals(var2.table.names.java_lang_Class)){ + ListBuffer var14 = new ListBuffer(); + Attribute[] var15 = var1.values; + int var16 = var15.length; + + for(var6 = 0; var6 < var16; ++var6){ + Attribute var7 = var15[var6]; + Type var8 = var7 instanceof UnresolvedClass ? ((UnresolvedClass)var7).classType : ((com.sun.tools.javac.code.Attribute.Class)var7).classType; + var14.append(var8); + } + + this.value = new AnnotationProxyMaker.MirroredTypesExceptionProxy(var14.toList()); + }else{ + int var3 = var1.values.length; + Class var4 = this.returnClass; + this.returnClass = this.returnClass.getComponentType(); + + try{ + Object var5 = java.lang.reflect.Array.newInstance(this.returnClass, var3); + + for(var6 = 0; var6 < var3; ++var6){ + var1.values[var6].accept(this); + if(this.value == null || this.value instanceof ExceptionProxy){ + return; + } + + try{ + java.lang.reflect.Array.set(var5, var6, this.value); + }catch(IllegalArgumentException var12){ + this.value = null; + return; + } + } + + this.value = var5; + }finally{ + this.returnClass = var4; + } + } + } + + public void visitEnum(Enum var1){ + if(this.returnClass.isEnum()){ + String var2 = var1.value.toString(); + + try{ + this.value = java.lang.Enum.valueOf((Class)this.returnClass, var2); + }catch(IllegalArgumentException var4){ + this.value = new EnumConstantNotPresentExceptionProxy((Class)this.returnClass, var2); + } + }else{ + this.value = null; + } + + } + + public void visitCompound(Compound var1){ + try{ + Class var2 = this.returnClass.asSubclass(Annotation.class); + this.value = AnnotationProxyMaker.generateAnnotation(var1, var2); + }catch(ClassCastException var3){ + this.value = null; + } + + } + + public void visitError(Error var1){ + if(var1 instanceof UnresolvedClass){ + this.value = new AnnotationProxyMaker.MirroredTypeExceptionProxy(((UnresolvedClass)var1).classType); + }else{ + this.value = null; + } + + } + + private void typeMismatch(Method var1, final Attribute var2){ + class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy{ + static final long serialVersionUID = 269L; + final transient Method method; + + AnnotationTypeMismatchExceptionProxy(Method var2x){ + this.method = var2x; + } + + public String toString(){ + return ""; + } + + protected RuntimeException generateException(){ + return new AnnotationTypeMismatchException(this.method, var2.type.toString()); + } + } + + this.value = new AnnotationTypeMismatchExceptionProxy(var1); + } + } +} diff --git a/annotations/src/main/java/mindustry/annotations/util/Selement.java b/annotations/src/main/java/mindustry/annotations/util/Selement.java index 94bf350009..d1f3e38e19 100644 --- a/annotations/src/main/java/mindustry/annotations/util/Selement.java +++ b/annotations/src/main/java/mindustry/annotations/util/Selement.java @@ -1,12 +1,15 @@ package mindustry.annotations.util; -import arc.struct.*; +import arc.struct.Array; import com.squareup.javapoet.*; +import com.sun.tools.javac.code.Attribute.*; import mindustry.annotations.*; import javax.lang.model.element.*; import javax.lang.model.type.*; +import java.lang.Class; import java.lang.annotation.*; +import java.lang.reflect.*; public class Selement{ public final T e; @@ -48,11 +51,18 @@ public class Selement{ } public A annotation(Class annotation){ - return e.getAnnotation(annotation); + try{ + Method m = com.sun.tools.javac.code.AnnoConstruct.class.getDeclaredMethod("getAttribute", Class.class); + m.setAccessible(true); + Compound compound = (Compound)m.invoke(e, annotation); + return compound == null ? null : AnnotationProxyMaker.generateAnnotation(compound, annotation); + }catch(Exception e){ + throw new RuntimeException(e); + } } public boolean has(Class annotation){ - return e.getAnnotation(annotation) != null; + return annotation(annotation) != null; } public Element up(){ diff --git a/build.gradle b/build.gradle index f8dc4a97c2..8aa9762606 100644 --- a/build.gradle +++ b/build.gradle @@ -163,6 +163,9 @@ allprojects{ project(":desktop"){ apply plugin: "java" + compileJava.options.fork = true + compileJava.options.compilerArgs += ["-XDignore.symbol.file"] + dependencies{ compile project(":core") diff --git a/core/src/mindustry/entities/def/AllEntities.java b/core/src/mindustry/entities/def/AllEntities.java index c8dee75ac2..2b452f0c5d 100644 --- a/core/src/mindustry/entities/def/AllEntities.java +++ b/core/src/mindustry/entities/def/AllEntities.java @@ -1,48 +1,49 @@ package mindustry.entities.def; import mindustry.annotations.Annotations.*; +import mindustry.gen.*; class AllEntities{ - @EntityDef(value = {BulletComp.class, VelComp.class, TimedComp.class}, pooled = true) + @EntityDef(value = {Bulletc.class, Velc.class, Timedc.class}, pooled = true) class BulletDef{} - @EntityDef(value = {TileComp.class}, isFinal = false) + @EntityDef(value = {Tilec.class}, isFinal = false) class TileDef{} - @EntityDef(value = {EffectComp.class}, pooled = true) + @EntityDef(value = {Effectc.class}, pooled = true) class EffectDef{} - @EntityDef({DecalComp.class}) + @EntityDef({Decalc.class}) class DecalDef{} - @EntityDef({PlayerComp.class}) + @EntityDef({Playerc.class}) class PlayerDef{} - @EntityDef({UnitComp.class}) + @EntityDef({Unitc.class}) class GenericUnitDef{} - @GroupDef(EntityComp.class) + @GroupDef(Entityc.class) void all(){ } - @GroupDef(PlayerComp.class) + @GroupDef(Playerc.class) void player(){ } - @GroupDef(value = UnitComp.class, spatial = true) + @GroupDef(value = Unitc.class, spatial = true) void unit(){ } - @GroupDef(TileComp.class) + @GroupDef(Tilec.class) void tile(){ } - @GroupDef(SyncComp.class) + @GroupDef(Syncc.class) void sync(){ }