mirror of
https://github.com/zaaarf/lillero-processor.git
synced 2024-11-14 05:39:19 +01:00
chore: added proper documentation, cleaned up build.gradle, added exceptions
This commit is contained in:
parent
eca2d1f312
commit
96b2bc5536
7 changed files with 147 additions and 36 deletions
|
@ -1,11 +1,8 @@
|
||||||
import org.gradle.internal.jvm.Jvm
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-gradle-plugin'
|
id 'java-gradle-plugin'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'ftbsc.lll.processor'
|
group 'ftbsc.lll.processor'
|
||||||
version '1.0-SNAPSHOT'
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -13,10 +10,9 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation files(Jvm.current().toolsJar)
|
|
||||||
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.0'
|
||||||
implementation 'org.ow2.asm:asm-commons:9.4'
|
implementation 'org.ow2.asm:asm-commons:9.4' //just for the javadocs
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
|
|
|
@ -4,8 +4,13 @@ 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.exceptions.MappingNotFoundException;
|
||||||
|
import ftbsc.lll.processor.exceptions.MappingsFileNotFoundException;
|
||||||
import ftbsc.lll.tools.DescriptorBuilder;
|
import ftbsc.lll.tools.DescriptorBuilder;
|
||||||
import ftbsc.lll.tools.SrgMapper;
|
import ftbsc.lll.tools.SrgMapper;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
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.ExecutableElement;
|
||||||
|
@ -27,22 +32,24 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual annotation processor behind the magic.
|
||||||
|
* It (implicitly) implements the {@link Processor} interface by extending {@link AbstractProcessor}.
|
||||||
|
*/
|
||||||
@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 {
|
||||||
|
|
||||||
private SrgMapper mapper;
|
/**
|
||||||
|
* Where the actual processing happens.
|
||||||
@Override
|
* It filters through whatever annotated class it's fed, and checks whether it contains
|
||||||
public void init(ProcessingEnvironment processingEnv) {
|
* the required information. It then generates injectors and a service provider for every
|
||||||
try {
|
* remaining class.
|
||||||
mapper = new SrgMapper(Files.lines(Paths.get("build/createMcpToSrg/output.tsrg")));
|
* @see LilleroProcessor#isValidInjector(TypeElement)
|
||||||
} catch(IOException e) {
|
* @param annotations the annotation types requested to be processed
|
||||||
throw new RuntimeException(e);
|
* @param roundEnv environment for information about the current and prior round
|
||||||
}
|
* @return whether or not the set of annotation types are claimed by this processor
|
||||||
super.init(processingEnv);
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
@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<>();
|
Set<TypeElement> validInjectors = new HashSet<>();
|
||||||
|
@ -66,7 +73,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
* It must have at least one method annotated with {@link Target}, and one method annotated with {@link Injector}
|
* It must have at least one method annotated with {@link Target}, and one method annotated with {@link Injector}
|
||||||
* that must be public, static and take in a ClassNode and a MethodNode from the ObjectWeb library.
|
* that must be public, static and take in a {@link ClassNode} and a {@link MethodNode}.
|
||||||
* @param elem the element to check.
|
* @param elem the element to check.
|
||||||
* @return whether it can be converted into a valid {@link IInjector}.
|
* @return whether it can be converted into a valid {@link IInjector}.
|
||||||
*/
|
*/
|
||||||
|
@ -77,6 +84,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.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
|
||||||
|
@ -85,6 +93,16 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds, among the methods of a class cl, the one annotated with ann, and tries to build
|
||||||
|
* a {@link MethodSpec} from it.
|
||||||
|
* In case of multiple occurrences, only the first one is returned.
|
||||||
|
* No check existance check is performed within the method.
|
||||||
|
* @param cl the {@link TypeElement} for the class containing the desired method
|
||||||
|
* @param ann the {@link Class} corresponding to the desired annotation
|
||||||
|
* @return the {@link MethodSpec} representing the desired method
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||||
private static MethodSpec findAnnotatedMethod(TypeElement cl, Class<? extends Annotation> ann) {
|
private static MethodSpec findAnnotatedMethod(TypeElement cl, Class<? extends Annotation> ann) {
|
||||||
return MethodSpec.overriding(
|
return MethodSpec.overriding(
|
||||||
(ExecutableElement) cl.getEnclosedElements()
|
(ExecutableElement) cl.getEnclosedElements()
|
||||||
|
@ -95,18 +113,19 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
).build();
|
).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getClassOrString(TypeName n) { //jank but fuck you
|
/**
|
||||||
return n.isPrimitive() ? n + ".class" : "\"" + n + "\"";
|
* Builds a type descriptor from the given {@link TypeName}
|
||||||
}
|
* @param type the {@link TypeName} representing the desired type
|
||||||
|
* @return a {@link String} containing the relevant descriptor
|
||||||
private static String descriptorFromType(TypeName type) {
|
*/
|
||||||
|
public static String descriptorFromType(TypeName type) {
|
||||||
StringBuilder desc = new StringBuilder();
|
StringBuilder desc = new StringBuilder();
|
||||||
//add array brackets
|
//add array brackets
|
||||||
while(type instanceof ArrayTypeName) {
|
while(type instanceof ArrayTypeName) {
|
||||||
desc.append("[");
|
desc.append("[");
|
||||||
type = ((ArrayTypeName) type).componentType;
|
type = ((ArrayTypeName) type).componentType;
|
||||||
}
|
}
|
||||||
if(type instanceof ClassName) { //todo maybe?
|
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(";");
|
desc.append(";");
|
||||||
|
@ -131,6 +150,11 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
return desc.toString();
|
return desc.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a method descriptor from the given {@link MethodSpec}.
|
||||||
|
* @param m the {@link MethodSpec} for the method
|
||||||
|
* @return a {@link String} containing the relevant descriptor
|
||||||
|
*/
|
||||||
public static String descriptorFromMethodSpec(MethodSpec m) {
|
public static String descriptorFromMethodSpec(MethodSpec m) {
|
||||||
StringBuilder methodSignature = new StringBuilder();
|
StringBuilder methodSignature = new StringBuilder();
|
||||||
methodSignature.append("(");
|
methodSignature.append("(");
|
||||||
|
@ -140,11 +164,24 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
return methodSignature.toString();
|
return methodSignature.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the Injector corresponding to the given class.
|
||||||
|
* Basically implements the {@link IInjector} interface for you.
|
||||||
|
* @param cl the {@link TypeElement} for the given class
|
||||||
|
*/
|
||||||
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);
|
MethodSpec targetMethod = findAnnotatedMethod(cl, Target.class);
|
||||||
MethodSpec injectorMethod = findAnnotatedMethod(cl, Injector.class);
|
MethodSpec injectorMethod = findAnnotatedMethod(cl, Injector.class);
|
||||||
|
|
||||||
|
SrgMapper mapper;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mapper = new SrgMapper(Files.lines(Paths.get("build/createMcpToSrg/output.tsrg")));
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new MappingsFileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
String packageName = cl.getQualifiedName().toString().replace("." + cl.getSimpleName().toString(), "");
|
String packageName = cl.getQualifiedName().toString().replace("." + cl.getSimpleName().toString(), "");
|
||||||
|
|
||||||
String className = cl.getQualifiedName().toString();
|
String className = cl.getQualifiedName().toString();
|
||||||
|
@ -165,20 +202,33 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
.addStatement("return $S", ann.reason())
|
.addStatement("return $S", ann.reason())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
//pretty sure class names de facto never change but better safe than sorry
|
||||||
|
String targetClassSrgName = mapper.getMcpClass(
|
||||||
|
ClassName.get(ann.value()).canonicalName().replace('.', '/')
|
||||||
|
);
|
||||||
|
|
||||||
|
if(targetClassSrgName == null)
|
||||||
|
throw new MappingNotFoundException(ClassName.get(ann.value()).canonicalName());
|
||||||
|
|
||||||
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", mapper.getMcpClass(ClassName.get(ann.value()).canonicalName().replace('.', '/')))
|
.addStatement("return $S", targetClassSrgName)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
String targetMethodDescriptor = descriptorFromMethodSpec(targetMethod);
|
String targetMethodDescriptor = descriptorFromMethodSpec(targetMethod);
|
||||||
|
String targetMethodSrgName = mapper.getSrgMember(
|
||||||
|
ann.value().getName(), targetMethod.name + " " + targetMethodDescriptor
|
||||||
|
);
|
||||||
|
|
||||||
|
if(targetMethodSrgName == null)
|
||||||
|
throw new MappingNotFoundException(targetMethod.name + " " + targetMethodDescriptor);
|
||||||
|
|
||||||
MethodSpec methodName = MethodSpec.methodBuilder("methodName")
|
MethodSpec methodName = MethodSpec.methodBuilder("methodName")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(String.class)
|
.returns(String.class)
|
||||||
.addStatement("return $S", mapper.getSrgMember(
|
.addStatement("return $S", targetMethodSrgName)
|
||||||
ann.value().getName(), targetMethod.name + " " + targetMethodDescriptor)
|
.build();
|
||||||
).build();
|
|
||||||
|
|
||||||
MethodSpec methodDesc = MethodSpec.methodBuilder("methodDesc")
|
MethodSpec methodDesc = MethodSpec.methodBuilder("methodDesc")
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
@ -189,8 +239,14 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
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((TypeName) processingEnv.getElementUtils().getTypeElement("org.objectweb.asm.tree.ClassNode").asType(), "clazz").build())
|
.addParameter(ParameterSpec.builder(
|
||||||
.addParameter(ParameterSpec.builder((TypeName) processingEnv.getElementUtils().getTypeElement("org.objectweb.asm.tree.MethodNode").asType(), "main").build())
|
(TypeName) processingEnv
|
||||||
|
.getElementUtils()
|
||||||
|
.getTypeElement("org.objectweb.asm.tree.ClassNode").asType(), "clazz").build())
|
||||||
|
.addParameter(ParameterSpec.builder(
|
||||||
|
(TypeName) processingEnv
|
||||||
|
.getElementUtils()
|
||||||
|
.getTypeElement("org.objectweb.asm.tree.MethodNode").asType(), "main").build())
|
||||||
.addStatement("$S.$S(clazz, main)", className, injectorMethod.name)
|
.addStatement("$S.$S(clazz, main)", className, injectorMethod.name)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -211,15 +267,24 @@ 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();
|
||||||
} catch(IOException e) {} //todo
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(Set<TypeElement> inj) {
|
||||||
try {
|
try {
|
||||||
FileObject serviceProvider = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", "ftbsc.lll.IInjector");
|
FileObject serviceProvider = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", "ftbsc.lll.IInjector");
|
||||||
PrintWriter out = new PrintWriter(serviceProvider.openWriter());
|
PrintWriter out = new PrintWriter(serviceProvider.openWriter());
|
||||||
inj.forEach(i -> out.println(i.getQualifiedName() + "Injector"));
|
inj.forEach(i -> out.println(i.getQualifiedName() + "Injector"));
|
||||||
out.close();
|
out.close();
|
||||||
} catch(IOException e) {} //todo
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,8 +3,19 @@ package ftbsc.lll.processor.annotations;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a method as the injector method for purposes of generation.
|
||||||
|
* The method itself should be {@code public static}, and take in a {@link ClassNode}
|
||||||
|
* and a {@link MethodNode} as parameters. It will be discarded otherwise.
|
||||||
|
* It will also be discarded unless the containing class is not annotated with {@link Patch}
|
||||||
|
* and no other method within the class is annotated with {@link Target}.
|
||||||
|
* @see Patch
|
||||||
|
* @see Target
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@Target(ElementType.METHOD)
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
public @interface Injector {}
|
public @interface Injector {}
|
||||||
|
|
|
@ -3,11 +3,24 @@ package ftbsc.lll.processor.annotations;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the class as containing an injector for a user-specified {@link Class}.
|
||||||
|
* It will be discarded unless {@link ftbsc.lll.processor.annotations.Target} and
|
||||||
|
* {@link Injector} are properly placed within.
|
||||||
|
* @see Target
|
||||||
|
* @see Injector
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@Target(ElementType.TYPE)
|
@java.lang.annotation.Target(ElementType.TYPE)
|
||||||
public @interface Patch {
|
public @interface Patch {
|
||||||
|
/**
|
||||||
|
* @return the Minecraft {@link Class} to target for patching
|
||||||
|
*/
|
||||||
Class<?> value();
|
Class<?> value();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the patching reason, for logging, defaults to "No reason specified."
|
||||||
|
*/
|
||||||
String reason() default "No reason specified.";
|
String reason() default "No reason specified.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,15 @@ import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a method as the target method.
|
||||||
|
* The method itself should have the same name, return type and parameters as the desired
|
||||||
|
* Minecraft method.
|
||||||
|
* It will also be discarded unless the containing class is not annotated with {@link Patch}
|
||||||
|
* and no other method within the class is annotated with {@link Injector}.
|
||||||
|
* @see Patch
|
||||||
|
* @see Injector
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@java.lang.annotation.Target(ElementType.METHOD)
|
@java.lang.annotation.Target(ElementType.METHOD)
|
||||||
public @interface Target {}
|
public @interface Target {}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ftbsc.lll.processor.exceptions;
|
||||||
|
|
||||||
|
public class MappingNotFoundException extends RuntimeException {
|
||||||
|
public MappingNotFoundException(String mapping) {
|
||||||
|
super("Could not find mapping for " + mapping + "!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ftbsc.lll.processor.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown upon failure to locate the output.tsrg file at runtime.
|
||||||
|
*/
|
||||||
|
public class MappingsFileNotFoundException extends RuntimeException {
|
||||||
|
public MappingsFileNotFoundException() {
|
||||||
|
super("Could not find a mappings file in the specified location!");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue