aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/ftbsc/lll')
-rw-r--r--src/main/java/ftbsc/lll/processor/LilleroProcessor.java119
-rw-r--r--src/main/java/ftbsc/lll/processor/annotations/Injector.java15
-rw-r--r--src/main/java/ftbsc/lll/processor/annotations/Patch.java17
-rw-r--r--src/main/java/ftbsc/lll/processor/annotations/Target.java9
-rw-r--r--src/main/java/ftbsc/lll/processor/exceptions/MappingNotFoundException.java7
-rw-r--r--src/main/java/ftbsc/lll/processor/exceptions/MappingsFileNotFoundException.java10
6 files changed, 146 insertions, 31 deletions
diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java
index a7c1962..1839183 100644
--- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java
+++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java
@@ -4,8 +4,13 @@ import com.squareup.javapoet.*;
import ftbsc.lll.IInjector;
import ftbsc.lll.processor.annotations.Injector;
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.SrgMapper;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.ExecutableElement;
@@ -27,22 +32,24 @@ import java.util.HashSet;
import java.util.List;
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")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class LilleroProcessor extends AbstractProcessor {
- private SrgMapper mapper;
-
- @Override
- public void init(ProcessingEnvironment processingEnv) {
- try {
- mapper = new SrgMapper(Files.lines(Paths.get("build/createMcpToSrg/output.tsrg")));
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
- super.init(processingEnv);
- }
-
+ /**
+ * Where the actual processing happens.
+ * It filters through whatever annotated class it's fed, and checks whether it contains
+ * the required information. It then generates injectors and a service provider for every
+ * remaining class.
+ * @see LilleroProcessor#isValidInjector(TypeElement)
+ * @param annotations the annotation types requested to be processed
+ * @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
+ */
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
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.
* 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.
* @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 -> {
List<? extends TypeMirror> params = ((ExecutableType) e.asType()).getParameterTypes();
return e.getAnnotation(Injector.class) != null
+ && e.getAnnotation(Target.class) != null
&& e.getModifiers().contains(Modifier.PUBLIC)
&& e.getModifiers().contains(Modifier.STATIC)
&& 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) {
return MethodSpec.overriding(
(ExecutableElement) cl.getEnclosedElements()
@@ -95,18 +113,19 @@ public class LilleroProcessor extends AbstractProcessor {
).build();
}
- private static String getClassOrString(TypeName n) { //jank but fuck you
- return n.isPrimitive() ? n + ".class" : "\"" + n + "\"";
- }
-
- private static String descriptorFromType(TypeName type) {
+ /**
+ * 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
+ */
+ public static String descriptorFromType(TypeName type) {
StringBuilder desc = new StringBuilder();
//add array brackets
while(type instanceof ArrayTypeName) {
desc.append("[");
type = ((ArrayTypeName) type).componentType;
}
- if(type instanceof ClassName) { //todo maybe?
+ if(type instanceof ClassName) {
ClassName var = (ClassName) type;
desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
desc.append(";");
@@ -131,6 +150,11 @@ public class LilleroProcessor extends AbstractProcessor {
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) {
StringBuilder methodSignature = new StringBuilder();
methodSignature.append("(");
@@ -140,11 +164,24 @@ public class LilleroProcessor extends AbstractProcessor {
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) {
Patch ann = cl.getAnnotation(Patch.class);
MethodSpec targetMethod = findAnnotatedMethod(cl, Target.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 className = cl.getQualifiedName().toString();
@@ -165,20 +202,33 @@ public class LilleroProcessor extends AbstractProcessor {
.addStatement("return $S", ann.reason())
.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")
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
- .addStatement("return $S", mapper.getMcpClass(ClassName.get(ann.value()).canonicalName().replace('.', '/')))
+ .addStatement("return $S", targetClassSrgName)
.build();
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")
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
- .addStatement("return $S", mapper.getSrgMember(
- ann.value().getName(), targetMethod.name + " " + targetMethodDescriptor)
- ).build();
+ .addStatement("return $S", targetMethodSrgName)
+ .build();
MethodSpec methodDesc = MethodSpec.methodBuilder("methodDesc")
.addModifiers(Modifier.PUBLIC)
@@ -189,8 +239,14 @@ public class LilleroProcessor extends AbstractProcessor {
MethodSpec inject = MethodSpec.methodBuilder("inject")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
- .addParameter(ParameterSpec.builder((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())
+ .addParameter(ParameterSpec.builder(
+ (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)
.build();
@@ -211,15 +267,24 @@ public class LilleroProcessor extends AbstractProcessor {
JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build();
javaFile.writeTo(out);
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) {
try {
FileObject serviceProvider = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", "ftbsc.lll.IInjector");
PrintWriter out = new PrintWriter(serviceProvider.openWriter());
inj.forEach(i -> out.println(i.getQualifiedName() + "Injector"));
out.close();
- } catch(IOException e) {} //todo
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
}
} \ No newline at end of file
diff --git a/src/main/java/ftbsc/lll/processor/annotations/Injector.java b/src/main/java/ftbsc/lll/processor/annotations/Injector.java
index 6184610..32c91c3 100644
--- a/src/main/java/ftbsc/lll/processor/annotations/Injector.java
+++ b/src/main/java/ftbsc/lll/processor/annotations/Injector.java
@@ -3,8 +3,19 @@ package ftbsc.lll.processor.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
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)
-@Target(ElementType.METHOD)
+@java.lang.annotation.Target(ElementType.METHOD)
public @interface Injector {}
diff --git a/src/main/java/ftbsc/lll/processor/annotations/Patch.java b/src/main/java/ftbsc/lll/processor/annotations/Patch.java
index adbb674..9b0f5da 100644
--- a/src/main/java/ftbsc/lll/processor/annotations/Patch.java
+++ b/src/main/java/ftbsc/lll/processor/annotations/Patch.java
@@ -3,11 +3,24 @@ package ftbsc.lll.processor.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
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)
-@Target(ElementType.TYPE)
+@java.lang.annotation.Target(ElementType.TYPE)
public @interface Patch {
+ /**
+ * @return the Minecraft {@link Class} to target for patching
+ */
Class<?> value();
+
+ /**
+ * @return the patching reason, for logging, defaults to "No reason specified."
+ */
String reason() default "No reason specified.";
}
diff --git a/src/main/java/ftbsc/lll/processor/annotations/Target.java b/src/main/java/ftbsc/lll/processor/annotations/Target.java
index e408755..14b4e7c 100644
--- a/src/main/java/ftbsc/lll/processor/annotations/Target.java
+++ b/src/main/java/ftbsc/lll/processor/annotations/Target.java
@@ -4,6 +4,15 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
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)
@java.lang.annotation.Target(ElementType.METHOD)
public @interface Target {}
diff --git a/src/main/java/ftbsc/lll/processor/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/processor/exceptions/MappingNotFoundException.java
new file mode 100644
index 0000000..ca1f75c
--- /dev/null
+++ b/src/main/java/ftbsc/lll/processor/exceptions/MappingNotFoundException.java
@@ -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 + "!");
+ }
+}
diff --git a/src/main/java/ftbsc/lll/processor/exceptions/MappingsFileNotFoundException.java b/src/main/java/ftbsc/lll/processor/exceptions/MappingsFileNotFoundException.java
new file mode 100644
index 0000000..8636226
--- /dev/null
+++ b/src/main/java/ftbsc/lll/processor/exceptions/MappingsFileNotFoundException.java
@@ -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!");
+ }
+}