1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-09-11 08:15:35 +03:00
This commit is contained in:
Anuken 2020-02-07 10:47:58 -05:00
parent 33e818a757
commit b9841f9cfa
5 changed files with 369 additions and 46 deletions

View File

@ -32,22 +32,46 @@ public class EntityProcess extends BaseProcessor{
ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>();
ObjectMap<Selement, Array<Stype>> defComponents = new ObjectMap<>();
ObjectMap<Svar, String> varInitializers = new ObjectMap<>();
ObjectMap<Smethod, String> methodBlocks = new ObjectMap<>();
ObjectSet<String> imports = new ObjectSet<>();
Array<Smethod> allGroups = new Array<>();
Array<Selement> allDefs = new Array<>();
Array<Stype> 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<Smethod> allGroups = methods(GroupDef.class);
Array<Selement> allDefs = elements(EntityDef.class);
Array<Stype> 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<Stype> 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<Stype> 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<Svar> 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<Stype> 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<Stype> dependencies = interf.allInterfaces().and(interf);
Array<Smethod> methods = dependencies.flatMap(Stype::methods);
@ -502,7 +524,7 @@ public class EntityProcess extends BaseProcessor{
Array<Stype> allComponents(Selement<?> type){
if(!defComponents.containsKey(type)){
//get base defs
Array<Stype> components = types(type.annotation(EntityDef.class), EntityDef::value);
Array<Stype> components = types(type.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);
ObjectSet<Stype> 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<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value);
Array<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);;
comps.sortComparing(Selement::name);
return comps.toString("", s -> s.name().replace("Comp", "")) + "Entity";
}

View File

@ -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<? extends Annotation> annoType;
private AnnotationProxyMaker(Compound var1, Class<? extends Annotation> var2){
this.anno = var1;
this.annoType = var2;
}
public static <A extends Annotation> A generateAnnotation(Compound var0, Class<A> 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<String, Object> 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<MethodSymbol, Attribute> 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<TypeMirror> types;
private final String typeStrings;
MirroredTypesExceptionProxy(List<TypeMirror> 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 "<error>";
}
protected RuntimeException generateException(){
return new AnnotationTypeMismatchException(this.method, var2.type.toString());
}
}
this.value = new AnnotationTypeMismatchExceptionProxy(var1);
}
}
}

View File

@ -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<T extends Element>{
public final T e;
@ -48,11 +51,18 @@ public class Selement<T extends Element>{
}
public <A extends Annotation> A annotation(Class<A> 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 <A extends Annotation> boolean has(Class<A> annotation){
return e.getAnnotation(annotation) != null;
return annotation(annotation) != null;
}
public Element up(){

View File

@ -163,6 +163,9 @@ allprojects{
project(":desktop"){
apply plugin: "java"
compileJava.options.fork = true
compileJava.options.compilerArgs += ["-XDignore.symbol.file"]
dependencies{
compile project(":core")

View File

@ -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(){
}