mirror of
https://github.com/zaaarf/lillero-processor.git
synced 2024-11-22 15:04:50 +01:00
feat: MVP!
This commit is contained in:
parent
25869ddc92
commit
252272ac42
2 changed files with 99 additions and 68 deletions
|
@ -3,14 +3,17 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'ftbsc.lll.processor'
|
group 'ftbsc.lll.processor'
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = 'https://maven.fantabos.co' }
|
maven { url = 'https://maven.fantabos.co' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: figure out how to make annotationProcessor inherit its dependencies
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.squareup:javapoet:1.13.0'
|
implementation 'com.squareup:javapoet:1.13.0'
|
||||||
implementation 'ftbsc:lll:0.2.0'
|
implementation 'ftbsc:lll:0.2.1'
|
||||||
implementation 'org.ow2.asm:asm-commons:9.4' //just for the javadocs
|
implementation 'org.ow2.asm:asm-commons:9.4'
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import com.squareup.javapoet.*;
|
||||||
import ftbsc.lll.IInjector;
|
import ftbsc.lll.IInjector;
|
||||||
import ftbsc.lll.processor.annotations.Injector;
|
import ftbsc.lll.processor.annotations.Injector;
|
||||||
import ftbsc.lll.processor.annotations.Patch;
|
import ftbsc.lll.processor.annotations.Patch;
|
||||||
|
import ftbsc.lll.processor.annotations.Target;
|
||||||
import ftbsc.lll.processor.exceptions.MappingNotFoundException;
|
import ftbsc.lll.processor.exceptions.MappingNotFoundException;
|
||||||
import ftbsc.lll.processor.exceptions.MappingsFileNotFoundException;
|
import ftbsc.lll.processor.exceptions.MappingsFileNotFoundException;
|
||||||
import ftbsc.lll.tools.DescriptorBuilder;
|
import ftbsc.lll.tools.DescriptorBuilder;
|
||||||
|
@ -13,24 +14,22 @@ import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.*;
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import javax.lang.model.element.TypeElement;
|
|
||||||
import javax.lang.model.type.ExecutableType;
|
import javax.lang.model.type.ExecutableType;
|
||||||
|
import javax.lang.model.type.MirroredTypeException;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
import javax.tools.FileObject;
|
import javax.tools.FileObject;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
import javax.tools.StandardLocation;
|
import javax.tools.StandardLocation;
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Target;
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual annotation processor behind the magic.
|
* The actual annotation processor behind the magic.
|
||||||
|
@ -39,6 +38,11 @@ import java.util.Set;
|
||||||
@SupportedAnnotationTypes("ftbsc.lll.processor.annotations.Patch")
|
@SupportedAnnotationTypes("ftbsc.lll.processor.annotations.Patch")
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
public class LilleroProcessor extends AbstractProcessor {
|
public class LilleroProcessor extends AbstractProcessor {
|
||||||
|
/**
|
||||||
|
* A {@link Set} of {@link String}s that will contain the fully qualified names
|
||||||
|
* of the generated injector files.
|
||||||
|
*/
|
||||||
|
private final Set<String> generatedInjectors = new HashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where the actual processing happens.
|
* Where the actual processing happens.
|
||||||
|
@ -52,23 +56,32 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
Set<TypeElement> validInjectors = new HashSet<>();
|
|
||||||
for (TypeElement annotation : annotations) {
|
for (TypeElement annotation : annotations) {
|
||||||
if(isValidInjector(annotation))
|
if(annotation.getQualifiedName().toString().equals(Patch.class.getName())) {
|
||||||
validInjectors.add(annotation);
|
Set<TypeElement> validInjectors = new HashSet<>();
|
||||||
|
Set<TypeElement> potentialInjectors =
|
||||||
|
roundEnv.getElementsAnnotatedWith(annotation)
|
||||||
|
.stream()
|
||||||
|
.map(e -> (TypeElement) e)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
for(TypeElement p : potentialInjectors)
|
||||||
|
if(isValidInjector(p))
|
||||||
|
validInjectors.add(p);
|
||||||
else processingEnv.getMessager().printMessage(
|
else processingEnv.getMessager().printMessage(
|
||||||
Diagnostic.Kind.WARNING,
|
Diagnostic.Kind.WARNING,
|
||||||
"Missing valid inject() method on @Injector class " + annotation.getQualifiedName() + "."
|
"Missing valid @Injector method in @Patch class " + p + ", skipping."
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if(validInjectors.isEmpty())
|
if(validInjectors.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
validInjectors.forEach(this::generateInjector);
|
validInjectors.forEach(this::generateInjector);
|
||||||
generateServiceProvider(validInjectors);
|
if(this.generatedInjectors.isEmpty())
|
||||||
|
return false;
|
||||||
|
generateServiceProvider();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This checks whether a given class contains the requirements to be parsed into a Lillero injector.
|
* This checks whether a given class contains the requirements to be parsed into a Lillero injector.
|
||||||
|
@ -84,7 +97,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
&& elem.getEnclosedElements().stream().anyMatch(e -> {
|
&& elem.getEnclosedElements().stream().anyMatch(e -> {
|
||||||
List<? extends TypeMirror> params = ((ExecutableType) e.asType()).getParameterTypes();
|
List<? extends TypeMirror> params = ((ExecutableType) e.asType()).getParameterTypes();
|
||||||
return e.getAnnotation(Injector.class) != null
|
return e.getAnnotation(Injector.class) != null
|
||||||
&& e.getAnnotation(Target.class) != null
|
&& e.getAnnotation(Target.class) == null
|
||||||
&& e.getModifiers().contains(Modifier.PUBLIC)
|
&& e.getModifiers().contains(Modifier.PUBLIC)
|
||||||
&& e.getModifiers().contains(Modifier.STATIC)
|
&& e.getModifiers().contains(Modifier.STATIC)
|
||||||
&& params.size() == 2
|
&& params.size() == 2
|
||||||
|
@ -103,22 +116,21 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
* @return the {@link MethodSpec} representing the desired method
|
* @return the {@link MethodSpec} representing the desired method
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||||
private static MethodSpec findAnnotatedMethod(TypeElement cl, Class<? extends Annotation> ann) {
|
private static ExecutableElement findAnnotatedMethod(TypeElement cl, Class<? extends Annotation> ann) {
|
||||||
return MethodSpec.overriding(
|
return (ExecutableElement) cl.getEnclosedElements()
|
||||||
(ExecutableElement) cl.getEnclosedElements()
|
|
||||||
.stream()
|
.stream()
|
||||||
.filter(e -> e.getAnnotation(ann) != null)
|
.filter(e -> e.getAnnotation(ann) != null)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.get() //will never be null so can ignore warning
|
.get(); //will never be null so can ignore warning
|
||||||
).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a type descriptor from the given {@link TypeName}
|
* Builds a type descriptor from the given {@link TypeMirror}
|
||||||
* @param type the {@link TypeName} representing the desired type
|
* @param t the {@link TypeMirror} representing the desired type
|
||||||
* @return a {@link String} containing the relevant descriptor
|
* @return a {@link String} containing the relevant descriptor
|
||||||
*/
|
*/
|
||||||
public static String descriptorFromType(TypeName type) {
|
public static String descriptorFromType(TypeMirror t) {
|
||||||
|
TypeName type = TypeName.get(t);
|
||||||
StringBuilder desc = new StringBuilder();
|
StringBuilder desc = new StringBuilder();
|
||||||
//add array brackets
|
//add array brackets
|
||||||
while(type instanceof ArrayTypeName) {
|
while(type instanceof ArrayTypeName) {
|
||||||
|
@ -128,7 +140,6 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
if(type instanceof ClassName) {
|
if(type instanceof ClassName) {
|
||||||
ClassName var = (ClassName) type;
|
ClassName var = (ClassName) type;
|
||||||
desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
|
desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
|
||||||
desc.append(";");
|
|
||||||
} else {
|
} else {
|
||||||
if(TypeName.BOOLEAN.equals(type))
|
if(TypeName.BOOLEAN.equals(type))
|
||||||
desc.append("Z");
|
desc.append("Z");
|
||||||
|
@ -146,21 +157,23 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
desc.append("J");
|
desc.append("J");
|
||||||
else if(TypeName.DOUBLE.equals(type))
|
else if(TypeName.DOUBLE.equals(type))
|
||||||
desc.append("D");
|
desc.append("D");
|
||||||
|
else if(TypeName.VOID.equals(type))
|
||||||
|
desc.append("V");
|
||||||
}
|
}
|
||||||
return desc.toString();
|
return desc.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a method descriptor from the given {@link MethodSpec}.
|
* Builds a method descriptor from the given {@link ExecutableElement}.
|
||||||
* @param m the {@link MethodSpec} for the method
|
* @param m the {@link ExecutableElement} for the method
|
||||||
* @return a {@link String} containing the relevant descriptor
|
* @return a {@link String} containing the relevant descriptor
|
||||||
*/
|
*/
|
||||||
public static String descriptorFromMethodSpec(MethodSpec m) {
|
public static String descriptorFromMethodSpec(ExecutableElement m) {
|
||||||
StringBuilder methodSignature = new StringBuilder();
|
StringBuilder methodSignature = new StringBuilder();
|
||||||
methodSignature.append("(");
|
methodSignature.append("(");
|
||||||
m.parameters.forEach(p -> methodSignature.append(descriptorFromType(p.type)));
|
m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType())));
|
||||||
methodSignature.append(")");
|
methodSignature.append(")");
|
||||||
methodSignature.append(descriptorFromType(m.returnType));
|
methodSignature.append(descriptorFromType(m.getReturnType()));
|
||||||
return methodSignature.toString();
|
return methodSignature.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,29 +184,34 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
*/
|
*/
|
||||||
private void generateInjector(TypeElement cl) {
|
private void generateInjector(TypeElement cl) {
|
||||||
Patch ann = cl.getAnnotation(Patch.class);
|
Patch ann = cl.getAnnotation(Patch.class);
|
||||||
MethodSpec targetMethod = findAnnotatedMethod(cl, Target.class);
|
ExecutableElement targetMethod = findAnnotatedMethod(cl, Target.class);
|
||||||
MethodSpec injectorMethod = findAnnotatedMethod(cl, Injector.class);
|
ExecutableElement injectorMethod = findAnnotatedMethod(cl, Injector.class);
|
||||||
|
|
||||||
SrgMapper mapper;
|
SrgMapper mapper;
|
||||||
|
|
||||||
try {
|
try { //TODO: cant we get it from local?
|
||||||
mapper = new SrgMapper(Files.lines(Paths.get("build/createMcpToSrg/output.tsrg")));
|
URL url = new URL("https://data.fantabos.co/output.tsrg");
|
||||||
|
InputStream is = url.openStream();
|
||||||
|
mapper = new SrgMapper(new BufferedReader(new InputStreamReader(is,
|
||||||
|
StandardCharsets.UTF_8)).lines());
|
||||||
|
is.close();
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
throw new MappingsFileNotFoundException();
|
throw new MappingsFileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
String packageName = cl.getQualifiedName().toString().replace("." + cl.getSimpleName().toString(), "");
|
Element packageElement = cl.getEnclosingElement();
|
||||||
|
while (packageElement.getKind() != ElementKind.PACKAGE)
|
||||||
|
packageElement = packageElement.getEnclosingElement();
|
||||||
|
String packageName = packageElement.toString();
|
||||||
|
|
||||||
String className = cl.getQualifiedName().toString();
|
|
||||||
String simpleClassName = cl.getSimpleName().toString();
|
String simpleClassName = cl.getSimpleName().toString();
|
||||||
|
|
||||||
String injectorClassName = className + "Injector";
|
|
||||||
String injectorSimpleClassName = simpleClassName + "Injector";
|
String injectorSimpleClassName = simpleClassName + "Injector";
|
||||||
|
String injectorClassName = packageName + "." + injectorSimpleClassName;
|
||||||
|
|
||||||
MethodSpec name = MethodSpec.methodBuilder("name")
|
MethodSpec name = MethodSpec.methodBuilder("name")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(String.class)
|
.returns(String.class)
|
||||||
.addStatement("return $S", simpleClassName)
|
.addStatement("return $S", injectorSimpleClassName)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
MethodSpec reason = MethodSpec.methodBuilder("reason")
|
MethodSpec reason = MethodSpec.methodBuilder("reason")
|
||||||
|
@ -202,27 +220,35 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
.addStatement("return $S", ann.reason())
|
.addStatement("return $S", ann.reason())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
String targetClassCanonicalName;
|
||||||
|
try {
|
||||||
|
targetClassCanonicalName = ann.value().getCanonicalName();
|
||||||
|
} catch(MirroredTypeException e) {
|
||||||
|
targetClassCanonicalName = e.getTypeMirror().toString();
|
||||||
|
}
|
||||||
|
|
||||||
//pretty sure class names de facto never change but better safe than sorry
|
//pretty sure class names de facto never change but better safe than sorry
|
||||||
String targetClassSrgName = mapper.getMcpClass(
|
String targetClassSrgName = mapper.getMcpClass(
|
||||||
ClassName.get(ann.value()).canonicalName().replace('.', '/')
|
targetClassCanonicalName.replace('.', '/')
|
||||||
);
|
);
|
||||||
|
|
||||||
if(targetClassSrgName == null)
|
if(targetClassSrgName == null)
|
||||||
throw new MappingNotFoundException(ClassName.get(ann.value()).canonicalName());
|
throw new MappingNotFoundException(targetClassCanonicalName);
|
||||||
|
|
||||||
MethodSpec targetClass = MethodSpec.methodBuilder("targetClass")
|
MethodSpec targetClass = MethodSpec.methodBuilder("targetClass")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(String.class)
|
.returns(String.class)
|
||||||
.addStatement("return $S", targetClassSrgName)
|
.addStatement("return $S", targetClassSrgName.replace('/', '.'))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
String targetMethodDescriptor = descriptorFromMethodSpec(targetMethod);
|
String targetMethodDescriptor = descriptorFromMethodSpec(targetMethod);
|
||||||
String targetMethodSrgName = mapper.getSrgMember(
|
String targetMethodSrgName = mapper.getSrgMember(
|
||||||
ann.value().getName(), targetMethod.name + " " + targetMethodDescriptor
|
targetClassCanonicalName.replace('.', '/'),
|
||||||
|
targetMethod.getSimpleName() + " " + targetMethodDescriptor
|
||||||
);
|
);
|
||||||
|
|
||||||
if(targetMethodSrgName == null)
|
if(targetMethodSrgName == null)
|
||||||
throw new MappingNotFoundException(targetMethod.name + " " + targetMethodDescriptor);
|
throw new MappingNotFoundException(targetMethod.getSimpleName() + " " + targetMethodDescriptor);
|
||||||
|
|
||||||
MethodSpec methodName = MethodSpec.methodBuilder("methodName")
|
MethodSpec methodName = MethodSpec.methodBuilder("methodName")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
@ -233,21 +259,21 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
MethodSpec methodDesc = MethodSpec.methodBuilder("methodDesc")
|
MethodSpec methodDesc = MethodSpec.methodBuilder("methodDesc")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(String.class)
|
.returns(String.class)
|
||||||
.addCode("return $S", targetMethodDescriptor)
|
.addCode("return $S;", targetMethodDescriptor)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
MethodSpec inject = MethodSpec.methodBuilder("inject")
|
MethodSpec inject = MethodSpec.methodBuilder("inject")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(void.class)
|
.returns(void.class)
|
||||||
.addParameter(ParameterSpec.builder(
|
.addParameter(ParameterSpec.builder(
|
||||||
(TypeName) processingEnv
|
TypeName.get(processingEnv
|
||||||
.getElementUtils()
|
.getElementUtils()
|
||||||
.getTypeElement("org.objectweb.asm.tree.ClassNode").asType(), "clazz").build())
|
.getTypeElement("org.objectweb.asm.tree.ClassNode").asType()), "clazz").build())
|
||||||
.addParameter(ParameterSpec.builder(
|
.addParameter(ParameterSpec.builder(
|
||||||
(TypeName) processingEnv
|
TypeName.get(processingEnv
|
||||||
.getElementUtils()
|
.getElementUtils()
|
||||||
.getTypeElement("org.objectweb.asm.tree.MethodNode").asType(), "main").build())
|
.getTypeElement("org.objectweb.asm.tree.MethodNode").asType()), "main").build())
|
||||||
.addStatement("$S.$S(clazz, main)", className, injectorMethod.name)
|
.addStatement("$T." + injectorMethod.getSimpleName() + "(clazz, main)", TypeName.get(cl.asType()))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
TypeSpec injectorClass = TypeSpec.classBuilder(injectorSimpleClassName)
|
TypeSpec injectorClass = TypeSpec.classBuilder(injectorSimpleClassName)
|
||||||
|
@ -267,6 +293,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build();
|
JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build();
|
||||||
javaFile.writeTo(out);
|
javaFile.writeTo(out);
|
||||||
out.close();
|
out.close();
|
||||||
|
this.generatedInjectors.add(injectorClassName);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -274,14 +301,15 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the Service Provider file for the generated injectors.
|
* Generates the Service Provider file for the generated injectors.
|
||||||
* It gets their names by appending "Injector" to the original class.
|
|
||||||
* @param inj a {@link Set} of {@link TypeElement} representing the valid injector generators
|
|
||||||
*/
|
*/
|
||||||
private void generateServiceProvider(Set<TypeElement> inj) {
|
private void generateServiceProvider() {
|
||||||
try {
|
try {
|
||||||
FileObject serviceProvider = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "ftbsc.lll.IInjector");
|
FileObject serviceProvider =
|
||||||
|
processingEnv.getFiler().createResource(
|
||||||
|
StandardLocation.CLASS_OUTPUT, "", "META-INF/services/ftbsc.lll.IInjector"
|
||||||
|
);
|
||||||
PrintWriter out = new PrintWriter(serviceProvider.openWriter());
|
PrintWriter out = new PrintWriter(serviceProvider.openWriter());
|
||||||
inj.forEach(i -> out.println(i.getQualifiedName() + "Injector"));
|
this.generatedInjectors.forEach(out::println);
|
||||||
out.close();
|
out.close();
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
Loading…
Reference in a new issue