From f6539d4a078e4cc37a56b9cdb548ba6a61a73b93 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 8 Mar 2023 15:37:17 +0100 Subject: feat: initial implementation of new system - multiple injectors/target - removed strict limitation of one patch per class - made finders far smarter - added boolean for obfuscation environment (currently never changed from default) --- .../exceptions/AmbiguousDefinitionException.java | 25 ++ .../lll/exceptions/MappingNotFoundException.java | 17 + .../lll/exceptions/TargetNotFoundException.java | 15 + src/main/java/ftbsc/lll/processor/ASTUtils.java | 126 ------ .../java/ftbsc/lll/processor/LilleroProcessor.java | 498 ++++++++++++++++----- .../ftbsc/lll/processor/annotations/FindField.java | 2 +- .../lll/processor/annotations/FindMethod.java | 4 +- .../ftbsc/lll/processor/annotations/Injector.java | 7 +- .../processor/annotations/MultipleInjectors.java | 11 + .../ftbsc/lll/processor/annotations/Target.java | 13 +- .../java/ftbsc/lll/processor/tools/ASTUtils.java | 151 +++++++ .../java/ftbsc/lll/processor/tools/SrgMapper.java | 211 +++++++++ 12 files changed, 829 insertions(+), 251 deletions(-) create mode 100644 src/main/java/ftbsc/lll/exceptions/AmbiguousDefinitionException.java create mode 100644 src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java create mode 100644 src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java delete mode 100644 src/main/java/ftbsc/lll/processor/ASTUtils.java create mode 100644 src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java create mode 100644 src/main/java/ftbsc/lll/processor/tools/ASTUtils.java create mode 100644 src/main/java/ftbsc/lll/processor/tools/SrgMapper.java (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/AmbiguousDefinitionException.java b/src/main/java/ftbsc/lll/exceptions/AmbiguousDefinitionException.java new file mode 100644 index 0000000..1befaa8 --- /dev/null +++ b/src/main/java/ftbsc/lll/exceptions/AmbiguousDefinitionException.java @@ -0,0 +1,25 @@ +package ftbsc.lll.exceptions; + +/** + * Thrown when the processor finds multiple methods matching the + * given criteria. + */ +public class AmbiguousDefinitionException extends RuntimeException { + + /** + * Constructs a new ambiguous definition exception with the specified detail message. + * @param message the detail message + */ + public AmbiguousDefinitionException(String message) { + super(message); + } + + /** + * Constructs a new ambiguous definition exception with the specified detail message and cause. + * @param message the detail message + * @param cause the cause, may be null (indicating nonexistent or unknown cause) + */ + public AmbiguousDefinitionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java new file mode 100644 index 0000000..817761b --- /dev/null +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -0,0 +1,17 @@ +package ftbsc.lll.exceptions; + +import ftbsc.lll.processor.tools.SrgMapper; + +/** + * Thrown upon failure to find the requested mapping within a loaded {@link SrgMapper}. + */ +public class MappingNotFoundException extends RuntimeException { + + /** + * Constructs a new mapping not found exception for the specified mapping. + * @param mapping the detail message + */ + public MappingNotFoundException(String mapping) { + super("Could not find mapping for " + mapping + "!"); + } +} diff --git a/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java new file mode 100644 index 0000000..5be3d77 --- /dev/null +++ b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java @@ -0,0 +1,15 @@ +package ftbsc.lll.exceptions; + +/** + * Thrown upon failure to find an existing method from a stub. + */ +public class TargetNotFoundException extends RuntimeException { + + /** + * Constructs a new target not found exception for the specified method stub. + * @param stub the stub's name (and descriptor possibly) + */ + public TargetNotFoundException(String stub) { + super("Could not find member corresponding to stub: " + stub); + } +} diff --git a/src/main/java/ftbsc/lll/processor/ASTUtils.java b/src/main/java/ftbsc/lll/processor/ASTUtils.java deleted file mode 100644 index eba5d89..0000000 --- a/src/main/java/ftbsc/lll/processor/ASTUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -package ftbsc.lll.processor; - -import com.squareup.javapoet.ArrayTypeName; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.TypeName; -import ftbsc.lll.tools.DescriptorBuilder; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; -import java.lang.annotation.Annotation; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Collection of static utils that didn't really fit into the main class. - */ -public class ASTUtils { - /** - * Finds, among the methods of a class cl, the one annotated with ann, and tries to build - * a {@link ExecutableElement} from it. - * @param cl the {@link ExecutableElement} for the class containing the desired method - * @param ann the {@link Class} corresponding to the desired annotation - * @return a {@link List} of {@link MethodSpec}s annotated with the given annotation - * @since 0.2.0 - */ - public static List findAnnotatedMethods(TypeElement cl, Class ann) { - return cl.getEnclosedElements() - .stream() - .filter(e -> e.getAnnotation(ann) != null) - .map(e -> (ExecutableElement) e) - .collect(Collectors.toList()); - } - - /** - * Builds a type descriptor from the given {@link TypeMirror} - * @param t the {@link TypeMirror} representing the desired type - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromType(TypeMirror t) { - TypeName type = TypeName.get(t); - StringBuilder desc = new StringBuilder(); - //add array brackets - while(type instanceof ArrayTypeName) { - desc.append("["); - type = ((ArrayTypeName) type).componentType; - } - if(type instanceof ClassName) { - ClassName var = (ClassName) type; - desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0)); - } else { - if(TypeName.BOOLEAN.equals(type)) - desc.append("Z"); - else if(TypeName.CHAR.equals(type)) - desc.append("C"); - else if(TypeName.BYTE.equals(type)) - desc.append("B"); - else if(TypeName.SHORT.equals(type)) - desc.append("S"); - else if(TypeName.INT.equals(type)) - desc.append("I"); - else if(TypeName.FLOAT.equals(type)) - desc.append("F"); - else if(TypeName.LONG.equals(type)) - desc.append("J"); - else if(TypeName.DOUBLE.equals(type)) - desc.append("D"); - else if(TypeName.VOID.equals(type)) - desc.append("V"); - } - return desc.toString(); - } - - /** - * Builds a method descriptor from the given {@link ExecutableElement}. - * @param m the {@link ExecutableElement} for the method - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromExecutableElement(ExecutableElement m) { - StringBuilder methodSignature = new StringBuilder(); - methodSignature.append("("); - m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType()))); - methodSignature.append(")"); - methodSignature.append(descriptorFromType(m.getReturnType())); - return methodSignature.toString(); - } - - /** - * Maps a {@link javax.lang.model.element.Modifier} to its reflective - * {@link java.lang.reflect.Modifier} equivalent. - * @param m the {@link Modifier} to map - * @return an integer representing the modifier - * @see java.lang.reflect.Modifier - * @since 0.2.0 - */ - public static int mapModifier(Modifier m) { - switch(m) { - case PUBLIC: - return java.lang.reflect.Modifier.PUBLIC; - case PROTECTED: - return java.lang.reflect.Modifier.PROTECTED; - case PRIVATE: - return java.lang.reflect.Modifier.PRIVATE; - case ABSTRACT: - return java.lang.reflect.Modifier.ABSTRACT; - case STATIC: - return java.lang.reflect.Modifier.STATIC; - case FINAL: - return java.lang.reflect.Modifier.FINAL; - case TRANSIENT: - return java.lang.reflect.Modifier.TRANSIENT; - case VOLATILE: - return java.lang.reflect.Modifier.VOLATILE; - case SYNCHRONIZED: - return java.lang.reflect.Modifier.SYNCHRONIZED; - case NATIVE: - return java.lang.reflect.Modifier.NATIVE; - case STRICTFP: - return java.lang.reflect.Modifier.STRICT; - default: - return 0; - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 501200a..0277211 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -2,33 +2,30 @@ package ftbsc.lll.processor; import com.squareup.javapoet.*; import ftbsc.lll.IInjector; +import ftbsc.lll.exceptions.AmbiguousDefinitionException; +import ftbsc.lll.exceptions.MappingNotFoundException; +import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.processor.annotations.*; +import ftbsc.lll.processor.tools.SrgMapper; import ftbsc.lll.proxies.FieldProxy; import ftbsc.lll.proxies.MethodProxy; -import ftbsc.lll.tools.SrgMapper; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.ExecutableType; -import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.*; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; -import static ftbsc.lll.processor.ASTUtils.*; +import static ftbsc.lll.processor.tools.ASTUtils.*; /** * The actual annotation processor behind the magic. @@ -43,6 +40,11 @@ public class LilleroProcessor extends AbstractProcessor { */ private final Set generatedInjectors = new HashSet<>(); + /** + * A static boolean that should be set to true when ran in a non-obfuscated environment. + */ + public static boolean obfuscatedEnvironment = false; //todo: set this + /** * Where the actual processing happens. * It filters through whatever annotated class it's fed, and checks whether it contains @@ -64,7 +66,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(this::isValidInjector) .collect(Collectors.toSet()); if(!validInjectors.isEmpty()) { - validInjectors.forEach(this::generateInjector); + validInjectors.forEach(this::generateInjectors); if (!this.generatedInjectors.isEmpty()) { generateServiceProvider(); return true; @@ -102,11 +104,190 @@ public class LilleroProcessor extends AbstractProcessor { } /** - * Generates the Injector corresponding to the given class. + * Finds the class name and maps it to the correct format. + * @param patchAnn the {@link Patch} annotation containing target class info + * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null + * @param mapper the {@link SrgMapper} to use + * @implNote De facto, there is never any difference between the SRG and MCP name of a class. + * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) + * names. However, this method still performs a conversion - just in case there is an + * odd one out. + * @return the fully qualified class name + * @since 0.3.0 + */ + private static String findClassName(Patch patchAnn, FindMethod methodAnn, SrgMapper mapper) { + String fullyQualifiedName = + methodAnn == null || methodAnn.parent() == Object.class + ? getClassFullyQualifiedName(patchAnn.value()) + : getClassFullyQualifiedName(methodAnn.parent()); + return mapper.mapClass(fullyQualifiedName, obfuscatedEnvironment).replace('/', '.'); + } + + /** + * Finds the class name and maps it to the correct format. + * @param patchAnn the {@link Patch} annotation containing target class info + * @param mapper the {@link SrgMapper} to use + * @return the internal class name + * @since 0.3.0 + */ + private static String findClassName(Patch patchAnn, SrgMapper mapper) { + return findClassName(patchAnn, null, mapper); + } + + /** + * Finds the method name and maps it to the correct format. + * @param parentFQN the already mapped FQN of the parent class + * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null + * @param stub the {@link ExecutableElement} for the stub + * @param mapper the {@link SrgMapper} to use + * @return the internal class name + * @since 0.3.0 + */ + private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); + try { + methodName = mapper.mapMember( + parentFQN, + methodName, + obfuscatedEnvironment + ); + } catch(MappingNotFoundException e) { + //not found: try again with the name of the annotated method + if(methodAnn == null) { + methodName = mapper.mapMember( + parentFQN, + stub.getSimpleName().toString(), + obfuscatedEnvironment + ); + } else throw e; + } + return methodName; + } + + /** + * Finds the method name and maps it to the correct format. + * @param patchAnn the {@link Patch} annotation containing target class info + * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null + * @param stub the {@link ExecutableElement} for the stub + * @param mapper the {@link SrgMapper} to use + * @return the internal class name + * @since 0.3.0 + */ + private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + return findMethodName(findClassName(patchAnn, methodAnn, mapper), methodAnn, stub, mapper); + } + + /** + * Finds a method given name, container and descriptor. + * @param fullyQualifiedNameParent the fully qualified name of the parent class of the method + * @param name the name to search for + * @param descr the descriptor to search for + * @param strict whether the search should be strict (see {@link Target#strict()} for more info) + * @return the desired method, if it exists + * @throws AmbiguousDefinitionException if it finds more than one candidate + * @throws TargetNotFoundException if it finds no valid candidate + * @since 0.3.0 + */ + private ExecutableElement findMethod(String fullyQualifiedNameParent, String name, String descr, boolean strict) { + TypeElement parent = processingEnv.getElementUtils().getTypeElement(fullyQualifiedNameParent); + if(parent == null) + throw new AmbiguousDefinitionException("Could not find parent class " + fullyQualifiedNameParent + "!"); + + //try to find by name + List candidates = parent.getEnclosedElements() + .stream() + .filter(e -> e instanceof ExecutableElement) + .map(e -> (ExecutableElement) e) + .filter(e -> e.getSimpleName().contentEquals(name)) + .collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(name + " " + descr); + if(candidates.size() == 1 && !strict) + return candidates.get(0); + if(descr == null) { + throw new AmbiguousDefinitionException( + "Found " + candidates.size() + + " methods named " + name + + " in class " + fullyQualifiedNameParent + "!" + ); + } else { + candidates = candidates.stream() + .filter(strict + ? c -> descr.equals(descriptorFromExecutableElement(c)) + : c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0]) + ).collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(name + " " + descr); + if(candidates.size() > 1) + throw new AmbiguousDefinitionException( + "Found " + candidates.size() + + " methods named " + name + + " in class " + fullyQualifiedNameParent + "!" + ); + return candidates.get(0); + } + } + + /** + * Finds the real method corresponding to a stub. + * @param stub the {@link ExecutableElement} for the stub + * @param mapper the {@link SrgMapper} to use + * @return the desired method, if it exists + * @throws AmbiguousDefinitionException if it finds more than one candidate + * @throws TargetNotFoundException if it finds no valid candidate + * @since 0.3.0 + */ + private ExecutableElement findRealMethod(ExecutableElement stub, SrgMapper mapper) { + Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); + FindMethod findAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info + Target target = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled + String parentFQN = findClassName(patchAnn, findAnn, mapper); + String methodName = findMethodName(patchAnn, findAnn, stub, mapper); + return findMethod( + parentFQN, + methodName, + descriptorFromExecutableElement(stub), + target != null && target.strict()); + } + + /** + * Finds the real field corresponding to a stub. + * @param stub the {@link ExecutableElement} for the stub + * @param mapper the {@link SrgMapper} to use + * @return the desired method, if it exists + * @throws TargetNotFoundException if it finds no valid candidate + * @since 0.3.0 + */ + private VariableElement findField(ExecutableElement stub, SrgMapper mapper) { + Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); + FindField fieldAnn = stub.getAnnotation(FindField.class); + String parentName = mapper.mapClass(getClassFullyQualifiedName( + fieldAnn.parent().equals(Object.class) + ? patchAnn.value() + : fieldAnn.parent() + ), obfuscatedEnvironment); + String name = fieldAnn.name().equals("") + ? stub.getSimpleName().toString() + : fieldAnn.name(); + TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentName); + List candidates = + parent.getEnclosedElements() + .stream() + .filter(f -> f instanceof VariableElement) + .filter(f -> f.getSimpleName().contentEquals(name)) + .map(f -> (VariableElement) f) + .collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(stub.getSimpleName().toString()); + else return candidates.get(0); //there can only ever be one + } + + /** + * Generates the Injector(s) contained in 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 generateInjectors(TypeElement cl) { SrgMapper mapper; try { //TODO: cant we get it from local? URL url = new URL("https://data.fantabos.co/output.tsrg"); @@ -116,79 +297,149 @@ public class LilleroProcessor extends AbstractProcessor { is.close(); } catch(IOException e) { throw new RuntimeException("Could not open the specified TSRG file!", e); - } + } //todo attempt to proceed without mappings - Patch ann = cl.getAnnotation(Patch.class); - 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 - String targetClassSrgName = mapper.getMcpClass(targetClassCanonicalName.replace('.', '/')); - - ExecutableElement targetMethod = findAnnotatedMethods(cl, Target.class).get(0); //there should only be one - String targetMethodDescriptor = descriptorFromMethodSpec(targetMethod); - String targetMethodSrgName = mapper.getSrgMember( - targetClassCanonicalName.replace('.', '/'), - targetMethod.getSimpleName() + " " + targetMethodDescriptor - ); - - ExecutableElement injectorMethod = findAnnotatedMethods(cl, Injector.class).get(0); //there should only be one + //find class information + Patch patchAnn = cl.getAnnotation(Patch.class); + String targetClassSrgName = findClassName(patchAnn, mapper); + //find package information Element packageElement = cl.getEnclosingElement(); while (packageElement.getKind() != ElementKind.PACKAGE) packageElement = packageElement.getEnclosingElement(); - String packageName = packageElement.toString(); - String injectorSimpleClassName = cl.getSimpleName().toString() + "Injector"; - String injectorClassName = packageName + "." + injectorSimpleClassName; - MethodSpec stubOverride = MethodSpec.overriding(targetMethod) - .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") - .build(); + //find injector(s) and target(s) + List injectors = findAnnotatedMethods(cl, MultipleInjectors.class); - MethodSpec inject = MethodSpec.methodBuilder("inject") - .addModifiers(Modifier.PUBLIC) - .returns(void.class) - .addAnnotation(Override.class) - .addParameter(ParameterSpec.builder( - TypeName.get(processingEnv - .getElementUtils() - .getTypeElement("org.objectweb.asm.tree.ClassNode").asType()), "clazz").build()) - .addParameter(ParameterSpec.builder( - TypeName.get(processingEnv - .getElementUtils() - .getTypeElement("org.objectweb.asm.tree.MethodNode").asType()), "main").build()) - .addStatement("super." + injectorMethod.getSimpleName() + "(clazz, main)", TypeName.get(cl.asType())) - .build(); + List targets = findAnnotatedMethods(cl, Target.class); - TypeSpec injectorClass = TypeSpec.classBuilder(injectorSimpleClassName) - .addModifiers(Modifier.PUBLIC) - .superclass(cl.asType()) - .addSuperinterface(ClassName.get(IInjector.class)) - .addMethod(buildStringReturnMethod("name", cl.getSimpleName().toString())) - .addMethod(buildStringReturnMethod("reason", ann.reason())) - .addMethod(buildStringReturnMethod("targetClass", targetClassSrgName.replace('/', '.'))) - .addMethod(buildStringReturnMethod("methodName", targetMethodSrgName)) - .addMethod(buildStringReturnMethod("methodDesc", targetMethodDescriptor)) - .addMethods(generateRequestedProxies(cl, mapper)) - .addMethod(stubOverride) - .addMethod(inject) - .build(); + //declare it once for efficiency + List targetNames = + targets.stream() + .map(ExecutableElement::getSimpleName) + .map(Object::toString) + .collect(Collectors.toList()); - JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build(); + //this will contain the classes to generate: the key is the class name + Map toGenerate = new HashMap<>(); - try { - JavaFileObject injectorFile = processingEnv.getFiler().createSourceFile(injectorClassName); - PrintWriter out = new PrintWriter(injectorFile.openWriter()); - javaFile.writeTo(out); - out.close(); - } catch(IOException e) { - throw new RuntimeException(e); + for(ExecutableElement inj : injectors) { + MultipleInjectors minjAnn = inj.getAnnotation(MultipleInjectors.class); + int iterationNumber = 1; + for(Injector injectorAnn : minjAnn.value()) { //java is dumb + List injectionCandidates = targets; + + //case 1: it has a name, try to match it + if(!injectorAnn.targetName().equals("") && targetNames.contains(injectorAnn.targetName())) + injectionCandidates = + injectionCandidates + .stream() + .filter(i -> i.getSimpleName().toString().equals(injectorAnn.targetName())) + .collect(Collectors.toList()); + + //case 2: try to match by injectTargetName + String inferredName = inj.getSimpleName() + .toString() + .replaceFirst("inject", ""); + injectionCandidates = + injectionCandidates + .stream() + .filter(t -> t.getSimpleName().toString().equalsIgnoreCase(inferredName)) + .collect(Collectors.toList()); + + //case 3: there is only one target + if(targets.size() == 1) + injectionCandidates.add(targets.get(0)); + + ExecutableElement injectionTarget = null; + + if(injectionCandidates.size() == 1) + injectionTarget = injectionCandidates.get(0); + + if(injectorAnn.params().length != 0) { + StringBuilder descr = new StringBuilder("("); + for(Class p : injectorAnn.params()) + descr.append(descriptorFromType(TypeName.get(p))); + descr.append(")"); + injectionCandidates = + injectionCandidates + .stream() + .filter(t -> //we care about arguments but not really about return type + descr.toString() + .split("\\)")[0] + .equalsIgnoreCase(descriptorFromExecutableElement(t).split("\\)")[0]) + ).collect(Collectors.toList()); + } + + if(injectionCandidates.size() == 1) + injectionTarget = injectionCandidates.get(0); + + //if we haven't found it yet, it's an ambiguity + if(injectionTarget == null) + throw new AmbiguousDefinitionException("Unclear target for injector " + inj.getSimpleName().toString() + "!"); + else toGenerate.put( + cl.getSimpleName().toString() + "Injector" + iterationNumber, + new InjectorInfo( + inj, findRealMethod( + injectionTarget, + mapper + ) + ) + ); + iterationNumber++; + } } - this.generatedInjectors.add(injectorClassName); + //iterate over the map and generate the classes + for(String injName : toGenerate.keySet()) { + MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).target) + .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") + .build(); + + MethodSpec inject = MethodSpec.methodBuilder("inject") + .addModifiers(Modifier.PUBLIC) + .returns(void.class) + .addAnnotation(Override.class) + .addParameter(ParameterSpec.builder( + TypeName.get(processingEnv + .getElementUtils() + .getTypeElement("org.objectweb.asm.tree.ClassNode").asType()), "clazz").build()) + .addParameter(ParameterSpec.builder( + TypeName.get(processingEnv + .getElementUtils() + .getTypeElement("org.objectweb.asm.tree.MethodNode").asType()), "main").build()) + .addStatement("super." + toGenerate.get(injName).injector.getSimpleName() + "(clazz, main)", TypeName.get(cl.asType())) + .build(); + + TypeSpec injectorClass = TypeSpec.classBuilder(injName) + .addModifiers(Modifier.PUBLIC) + .superclass(cl.asType()) + .addSuperinterface(ClassName.get(IInjector.class)) + .addMethod(buildStringReturnMethod("name", cl.getSimpleName().toString())) + .addMethod(buildStringReturnMethod("reason", patchAnn.reason())) + .addMethod(buildStringReturnMethod("targetClass", targetClassSrgName.replace('/', '.'))) + .addMethod(buildStringReturnMethod("methodName", toGenerate.get(injName).target.getSimpleName().toString())) + .addMethod(buildStringReturnMethod("methodDesc", descriptorFromExecutableElement(toGenerate.get(injName).target))) + .addMethods(generateRequestedProxies(cl, mapper)) + .addMethod(stubOverride) + .addMethod(inject) + .build(); + + JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build(); + String injectorClassName = packageName + "." + injName; + + try { + JavaFileObject injectorFile = processingEnv.getFiler().createSourceFile(injectorClassName); + PrintWriter out = new PrintWriter(injectorFile.openWriter()); + javaFile.writeTo(out); + out.close(); + } catch(IOException e) { + throw new RuntimeException(e); + } + + this.generatedInjectors.add(injectorClassName); + } } /** @@ -220,64 +471,44 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { - FindMethod ann = m.getAnnotation(FindMethod.class); - String targetMethodName = ann.name().equals("") ? m.getSimpleName().toString() : ann.name(); - try { - MethodSpec.Builder b = MethodSpec.overriding(m); - Method targetMethod = ann.parent().getMethod( - targetMethodName, - ann.params() - ); - b.addStatement("$T bd = $T.builder($S)", - MethodProxy.Builder.class, - MethodProxy.class, - targetMethodName - ); - b.addStatement("bd.setParent($S)", targetMethod.getDeclaringClass().getCanonicalName()); - b.addStatement("bd.setModifier($L)", targetMethod.getModifiers()); - for(Class p : targetMethod.getParameterTypes()) - b.addStatement("bd.addParameter($T.class)", p); - b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); - b.addStatement("return bd.build()"); - generated.add(b.build()); - } catch(NoSuchMethodException e) { - processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, - "Method not found: " + targetMethodName - ); - } + ExecutableElement targetMethod = findRealMethod(m, mapper); + MethodSpec.Builder b = MethodSpec.overriding(m); + b.addStatement("$T bd = $T.builder($S)", + MethodProxy.Builder.class, + MethodProxy.class, + m.getSimpleName().toString() + ); + b.addStatement("bd.setParent($S)", ((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString()); + for(Modifier mod : targetMethod.getModifiers()) + b.addStatement("bd.addModifier($L)", mapModifier(mod)); + for(TypeParameterElement p : targetMethod.getTypeParameters()) + b.addStatement("bd.addParameter($T.class)", p.asType()); + b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); + b.addStatement("return bd.build()"); + generated.add(b.build()); }); findAnnotatedMethods(cl, FindField.class) .stream() .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { - FindField ann = m.getAnnotation(FindField.class); - String targetFieldName = ann.name().equals("") ? m.getSimpleName().toString() : ann.name(); - try { - MethodSpec.Builder b = MethodSpec.overriding(m); - Field targetField = ann.parent().getField(targetFieldName); - b.addStatement("$T bd = $T.builder($S)", - FieldProxy.Builder.class, - FieldProxy.class, - targetFieldName - ); - b.addStatement("bd.setParent($S)", targetField.getDeclaringClass().getCanonicalName()); - b.addStatement("bd.setModifier($L)", targetField.getModifiers()); - b.addStatement("bd.setType($T.class)", targetField.getType()); - b.addStatement("return bd.build()"); - generated.add(b.build()); - } catch(NoSuchFieldException e) { - processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, - "Field not found: " + targetFieldName + " in class " + ann.parent().getCanonicalName() - ); - } + VariableElement targetField = findField(m, mapper); + MethodSpec.Builder b = MethodSpec.overriding(m); + b.addStatement("$T bd = $T.builder($S)", + FieldProxy.Builder.class, + FieldProxy.class, + targetField.getSimpleName().toString() + ); + b.addStatement("bd.setParent($S)", ((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString()); + for(Modifier mod : targetField.getModifiers()) + b.addStatement("bd.addModifier($L)", mapModifier(mod)); + b.addStatement("bd.setType($T.class)", targetField.asType()); + b.addStatement("return bd.build()"); + generated.add(b.build()); }); return generated; } - /** * Generates the Service Provider file for the generated injectors. */ @@ -294,4 +525,31 @@ public class LilleroProcessor extends AbstractProcessor { throw new RuntimeException(e); } } + + /** + * Container for information about a class that is to be generated. + * Only used internally. + */ + private static class InjectorInfo { + /** + * The {@link ExecutableElement} corresponding to the injector method. + */ + public final ExecutableElement injector; + + + /** + * The {@link ExecutableElement} corresponding to the target method. + */ + public final ExecutableElement target; + + /** + * Public constructor. + * @param injector the injector {@link ExecutableElement} + * @param target the target {@link ExecutableElement} + */ + public InjectorInfo(ExecutableElement injector, ExecutableElement target) { + this.injector = injector; + this.target = target; + } + } } \ No newline at end of file diff --git a/src/main/java/ftbsc/lll/processor/annotations/FindField.java b/src/main/java/ftbsc/lll/processor/annotations/FindField.java index 9b5a824..c13fd46 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/FindField.java +++ b/src/main/java/ftbsc/lll/processor/annotations/FindField.java @@ -16,6 +16,6 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) public @interface FindField { - Class parent(); + Class parent() default Object.class; String name() default ""; } diff --git a/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java b/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java index bf93442..76fe560 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java +++ b/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java @@ -17,7 +17,7 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) public @interface FindMethod { - Class parent(); + Class parent() default Object.class; String name() default ""; - Class[] params(); + Class[] params() default {}; } diff --git a/src/main/java/ftbsc/lll/processor/annotations/Injector.java b/src/main/java/ftbsc/lll/processor/annotations/Injector.java index f5e22aa..c26f704 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/Injector.java +++ b/src/main/java/ftbsc/lll/processor/annotations/Injector.java @@ -1,6 +1,7 @@ package ftbsc.lll.processor.annotations; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -14,5 +15,9 @@ import java.lang.annotation.RetentionPolicy; * @see Target */ @Retention(RetentionPolicy.CLASS) +@Repeatable(MultipleInjectors.class) @java.lang.annotation.Target(ElementType.METHOD) -public @interface Injector {} +public @interface Injector { + String targetName() default ""; + Class[] params() default {}; +} diff --git a/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java b/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java new file mode 100644 index 0000000..5c6382e --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java @@ -0,0 +1,11 @@ +package ftbsc.lll.processor.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.CLASS) +@java.lang.annotation.Target(ElementType.METHOD) +public @interface MultipleInjectors { + Injector[] value(); +} diff --git a/src/main/java/ftbsc/lll/processor/annotations/Target.java b/src/main/java/ftbsc/lll/processor/annotations/Target.java index 885bd9c..38477d1 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/Target.java +++ b/src/main/java/ftbsc/lll/processor/annotations/Target.java @@ -15,4 +15,15 @@ import java.lang.annotation.RetentionPolicy; */ @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) -public @interface Target {} +public @interface Target { + + /** + * When set to false, tells the processor to first try to match a single method by name, + * and to only check parameters if further clarification is needed. + * @implNote While non-strict mode is more computationally efficient, it's ultimately not + * relevant, as it only matters at compile time. Do not set this to true unless + * you are sure know what you're doing. + * @since 0.3.0 + */ + boolean strict() default true; +} diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java new file mode 100644 index 0000000..c35e008 --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -0,0 +1,151 @@ +package ftbsc.lll.processor.tools; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; +import ftbsc.lll.tools.DescriptorBuilder; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.TypeMirror; +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Collection of static utils that didn't really fit into the main class. + */ +public class ASTUtils { + /** + * Finds, among the methods of a class cl, the one annotated with ann, and tries to build + * a {@link ExecutableElement} from it. + * @param cl the {@link ExecutableElement} for the class containing the desired method + * @param ann the {@link Class} corresponding to the desired annotation + * @return a {@link List} of {@link MethodSpec}s annotated with the given annotation + * @since 0.2.0 + */ + public static List findAnnotatedMethods(TypeElement cl, Class ann) { + return cl.getEnclosedElements() + .stream() + .filter(e -> e.getAnnotation(ann) != null) + .map(e -> (ExecutableElement) e) + .collect(Collectors.toList()); + } + + /** + * 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) { + ClassName var = (ClassName) type; + desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0)); + } else { + if(TypeName.BOOLEAN.equals(type)) + desc.append("Z"); + else if(TypeName.CHAR.equals(type)) + desc.append("C"); + else if(TypeName.BYTE.equals(type)) + desc.append("B"); + else if(TypeName.SHORT.equals(type)) + desc.append("S"); + else if(TypeName.INT.equals(type)) + desc.append("I"); + else if(TypeName.FLOAT.equals(type)) + desc.append("F"); + else if(TypeName.LONG.equals(type)) + desc.append("J"); + else if(TypeName.DOUBLE.equals(type)) + desc.append("D"); + else if(TypeName.VOID.equals(type)) + desc.append("V"); + } + return desc.toString(); + } + + /** + * Builds a type descriptor from the given {@link TypeMirror}. + * @param t the {@link TypeMirror} representing the desired type + * @return a {@link String} containing the relevant descriptor + */ + public static String descriptorFromType(TypeMirror t) { + return descriptorFromType(TypeName.get(t)); + } + + /** + * Builds a method descriptor from the given {@link ExecutableElement}. + * @param m the {@link ExecutableElement} for the method + * @return a {@link String} containing the relevant descriptor + */ + public static String descriptorFromExecutableElement(ExecutableElement m) { + StringBuilder methodSignature = new StringBuilder(); + methodSignature.append("("); + m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType()))); + methodSignature.append(")"); + methodSignature.append(descriptorFromType(m.getReturnType())); + return methodSignature.toString(); + } + + /** + * Maps a {@link javax.lang.model.element.Modifier} to its reflective + * {@link java.lang.reflect.Modifier} equivalent. + * @param m the {@link Modifier} to map + * @return an integer representing the modifier + * @see java.lang.reflect.Modifier + * @since 0.2.0 + */ + public static int mapModifier(Modifier m) { + switch(m) { + case PUBLIC: + return java.lang.reflect.Modifier.PUBLIC; + case PROTECTED: + return java.lang.reflect.Modifier.PROTECTED; + case PRIVATE: + return java.lang.reflect.Modifier.PRIVATE; + case ABSTRACT: + return java.lang.reflect.Modifier.ABSTRACT; + case STATIC: + return java.lang.reflect.Modifier.STATIC; + case FINAL: + return java.lang.reflect.Modifier.FINAL; + case TRANSIENT: + return java.lang.reflect.Modifier.TRANSIENT; + case VOLATILE: + return java.lang.reflect.Modifier.VOLATILE; + case SYNCHRONIZED: + return java.lang.reflect.Modifier.SYNCHRONIZED; + case NATIVE: + return java.lang.reflect.Modifier.NATIVE; + case STRICTFP: + return java.lang.reflect.Modifier.STRICT; + default: + return 0; + } + } + + /** + * Safely converts a {@link Class} to its fully qualified name. See + * this blogpost + * for more information. + * @param clazz the class to get the name for + * @return the fully qualified name of the given class + * @since 0.3.0 + */ + public static String getClassFullyQualifiedName(Class clazz) { + try { + return clazz.getCanonicalName(); + } catch(MirroredTypeException e) { + return e.getTypeMirror().toString(); + } + } +} diff --git a/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java b/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java new file mode 100644 index 0000000..d3c9f79 --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java @@ -0,0 +1,211 @@ +package ftbsc.lll.processor.tools; + +import ftbsc.lll.exceptions.MappingNotFoundException; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +/** + * Parses a .tsrg file into a mapper capable of converting from + * deobfuscated names to SRG names. + * Obviously, it may only be used at runtime if the .tsrg file is + * included in the resources. However, in that case, I'd recommend + * using the built-in Forge one and refrain from including an extra + * resource for no good reason. + * @since 0.2.0 + */ +public class SrgMapper { + + /** + * A Map using the deobfuscated names as keys, + * holding information for that Srg class as value. + */ + private final Map mapper = new HashMap<>(); + + /** + * The public constructor. + * Should be passed a {@link Stream} of Strings, one representing each line. + * Whether they contain line endings or not is irrelevant. + * @param str a {@link Stream} of strings + */ + public SrgMapper(Stream str) { + AtomicReference currentClass = new AtomicReference<>(""); + str.forEach(l -> { + if(l.startsWith("\t")) + mapper.get(currentClass.get()).addMember(l); + else { + ObfuscationData s = new ObfuscationData(l); + currentClass.set(s.mcpName); + mapper.put(s.mcpName, s); + } + }); + } + + /** + * Gets the SRG-obfuscated name of the class. + * @param mcp the MCP (deobfuscated) internal name of the desired class + * @return the SRG name of the class + * @throws MappingNotFoundException if no mapping is found + */ + public String getSrgClass(String mcp) { + ObfuscationData data = mapper.get(mcp); + if(data == null) + throw new MappingNotFoundException(mcp); + else return data.srgName; + } + + /** + * Gets the MCP (deobfuscated) name of the class. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param srg the SRG-obfuscated internal name of the desired class + * @return the MCP name of the class + */ + public String getMcpClass(String srg) { + ObfuscationData data = getObfuscationData(srg); + return data.mcpName; + } + + /** + * Gets one between the SRG and MCP names. + * @param name the internal name of the desired class in either format + * @param obf whether it should return the obfuscated name + * @return a {@link String} containing the internal name of the class + * @throws MappingNotFoundException if no mapping is found + * @since 0.3.0 + */ + public String mapClass(String name, boolean obf) { + String srg; + try { + srg = this.getSrgClass(name); + } catch(MappingNotFoundException e) { + srg = name; + name = this.getMcpClass(srg); + } + if(obf) return srg; + else return name; + } + + /** + * Gets the SRG-obfuscated name of a class member (field or method). + * The method signature must be in this format: "methodName methodDescriptor", + * with a space, because that's how it is in .tsrg files. + * @param mcpClass the MCP (deobfuscated) internal name of the container class + * @param member the field name or method signature + * @return the SRG name of the given member + * @throws MappingNotFoundException if no mapping is found + */ + public String getSrgMember(String mcpClass, String member) { + ObfuscationData data = mapper.get(mcpClass); + if(data == null) + throw new MappingNotFoundException(mcpClass + "::" + member); + return data.members.get(member); + } + + /** + * Gets the MCP (deobfuscated) name of the given member. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param srgClass the SRG-obfuscated internal name of the container class + * @param member the field name or method signature + * @return the MCP name of the given member + */ + public String getMcpMember(String srgClass, String member) { + ObfuscationData data = getObfuscationData(srgClass); + for(String mcp : data.members.keySet()) + if(data.members.get(mcp).equals(member)) + return mcp; + return null; + } + + /** + * Obfuscates or deobfuscates a member, given one of its names and the effective. + * @param className the internal or fully qualified name of the container class + * @param memberName the member of the class + * @param obf whether it should return the obfuscated name + * @return the mapped member name + * @throws MappingNotFoundException if no mapping is found + * @since 0.3.0 + */ + public String mapMember(String className, String memberName, boolean obf) { + className = className.replace('.', '/'); + String effectiveClassName = this.mapClass(className, obf); + String srgMemberName; + try { + srgMemberName = this.getSrgMember(effectiveClassName, memberName); + } catch(MappingNotFoundException e) { + srgMemberName = memberName; + memberName = this.getMcpMember(effectiveClassName, memberName); + } + if(obf) return srgMemberName; + else return memberName; + } + + /** + * Used internally. Gets the obfuscation data corresponding to the given SRG name. + * @return the desired {@link ObfuscationData} object + * @throws MappingNotFoundException if no {@link ObfuscationData} object is found + */ + private ObfuscationData getObfuscationData(String srg) { + for(ObfuscationData s : mapper.values()) + if(s.srgName.equals(srg)) + return s; + throw new MappingNotFoundException(srg); + } + + /** + * Private class used internally for storing information about each + * class. It's private because there is no good reason anyone would + * want to access this outside of this class. + */ + private static class ObfuscationData { + /** + * The MCP internal name (FQN with '/' instad of '.') of the class. + */ + private final String mcpName; + + /** + * The SRG internal name (FQN with '/' instad of '.') of the class. + */ + private final String srgName; + + /** + * A {@link Map} tying each member's deobfuscated name or signature to its + * SRG name. + */ + private final Map members; + + + /** + * The constructor. It takes in the line where the class is declared, + * which looks something like this: + * {@code internal/name/mcp internal/name/srg } + * @param s the String represeting the declaration line + */ + private ObfuscationData(String s) { + String[] split = s.trim().split(" "); + this.mcpName = split[0]; + this.srgName = split[1]; + this.members = new HashMap<>(); + } + + /** + * Adds a member to the target class. It takes in the line where the + * member is declared. + * For fields it looks like this: + * {@code fieldMcpName field_srg_name} + * For methods it looks like this: + * {@code methodName methodDescriptor method_srg_name} + * @param s the String representing the declaration line + */ + public void addMember(String s) { + String[] split = s.trim().split(" "); + if(split.length == 2) //field + members.put(split[0], split[1]); + else if (split.length == 3) //method + members.put(split[0] + " " + split[1], split[2]); + } + } +} -- cgit v1.2.3-56-ga3b1 From 036dec2bb2bee7a0e0fda2689a9381303e8f6996 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 8 Mar 2023 15:53:48 +0100 Subject: feat: no-mappings-available mode --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 57 ++++++++++++++-------- 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 0277211..7d2645e 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -103,11 +103,26 @@ public class LilleroProcessor extends AbstractProcessor { } } + /** + * Finds the class name and maps it to the correct format. + * @param name the fully qualified name of the class to convert + * @param mapper the {@link SrgMapper} to use, may be null + * @implNote De facto, there is never any difference between the SRG and MCP name of a class. + * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) + * names. However, this method still performs a conversion - just in case there is an + * odd one out. + * @return the fully qualified class name + * @since 0.3.0 + */ + private static String findClassName(String name, SrgMapper mapper) { + return mapper == null ? name : mapper.mapClass(name, obfuscatedEnvironment).replace('/', '.'); + } + /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link SrgMapper} to use, may be null * @implNote De facto, there is never any difference between the SRG and MCP name of a class. * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) * names. However, this method still performs a conversion - just in case there is an @@ -120,13 +135,13 @@ public class LilleroProcessor extends AbstractProcessor { methodAnn == null || methodAnn.parent() == Object.class ? getClassFullyQualifiedName(patchAnn.value()) : getClassFullyQualifiedName(methodAnn.parent()); - return mapper.mapClass(fullyQualifiedName, obfuscatedEnvironment).replace('/', '.'); + return findClassName(fullyQualifiedName, mapper); } /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link SrgMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ @@ -134,31 +149,35 @@ public class LilleroProcessor extends AbstractProcessor { return findClassName(patchAnn, null, mapper); } + /** + * Finds the member name and maps it to the correct format. + * @param parentFQN the already mapped FQN of the parent class + * @param memberName the name of the member + * @param mapper the {@link SrgMapper} to use, may be null + * @return the internal class name + * @since 0.3.0 + */ + private static String findMemberName(String parentFQN, String memberName, SrgMapper mapper) { + return mapper == null ? memberName : mapper.mapMember(parentFQN, memberName, obfuscatedEnvironment); + } + /** * Finds the method name and maps it to the correct format. * @param parentFQN the already mapped FQN of the parent class * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link SrgMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); try { - methodName = mapper.mapMember( - parentFQN, - methodName, - obfuscatedEnvironment - ); + methodName = findMemberName(parentFQN, methodName, mapper); } catch(MappingNotFoundException e) { //not found: try again with the name of the annotated method if(methodAnn == null) { - methodName = mapper.mapMember( - parentFQN, - stub.getSimpleName().toString(), - obfuscatedEnvironment - ); + methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), mapper); } else throw e; } return methodName; @@ -261,11 +280,11 @@ public class LilleroProcessor extends AbstractProcessor { private VariableElement findField(ExecutableElement stub, SrgMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindField fieldAnn = stub.getAnnotation(FindField.class); - String parentName = mapper.mapClass(getClassFullyQualifiedName( + String parentName = findClassName(getClassFullyQualifiedName( fieldAnn.parent().equals(Object.class) ? patchAnn.value() : fieldAnn.parent() - ), obfuscatedEnvironment); + ), mapper); String name = fieldAnn.name().equals("") ? stub.getSimpleName().toString() : fieldAnn.name(); @@ -288,16 +307,14 @@ public class LilleroProcessor extends AbstractProcessor { * @param cl the {@link TypeElement} for the given class */ private void generateInjectors(TypeElement cl) { - SrgMapper mapper; + SrgMapper mapper = null; try { //TODO: cant we get it from local? 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) { - throw new RuntimeException("Could not open the specified TSRG file!", e); - } //todo attempt to proceed without mappings + } catch(IOException ignored) {} //TODO: proper handling //find class information Patch patchAnn = cl.getAnnotation(Patch.class); -- cgit v1.2.3-56-ga3b1 From df3590d097e28309f7ed2dc3cc3d8413a9f56b49 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 8 Mar 2023 18:33:06 +0100 Subject: feat: mappings can now be taken in as an option, generified SrgMapper --- .../lll/exceptions/MappingNotFoundException.java | 4 +- .../java/ftbsc/lll/processor/LilleroProcessor.java | 80 +++++--- .../java/ftbsc/lll/processor/tools/SrgMapper.java | 211 --------------------- .../tools/obfuscation/ObfuscationMapper.java | 167 ++++++++++++++++ 4 files changed, 218 insertions(+), 244 deletions(-) delete mode 100644 src/main/java/ftbsc/lll/processor/tools/SrgMapper.java create mode 100644 src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java index 817761b..a1a47d1 100644 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -1,9 +1,9 @@ package ftbsc.lll.exceptions; -import ftbsc.lll.processor.tools.SrgMapper; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; /** - * Thrown upon failure to find the requested mapping within a loaded {@link SrgMapper}. + * Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}. */ public class MappingNotFoundException extends RuntimeException { diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 7d2645e..c6343a6 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -6,7 +6,7 @@ import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.MappingNotFoundException; import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.processor.annotations.*; -import ftbsc.lll.processor.tools.SrgMapper; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import ftbsc.lll.proxies.FieldProxy; import ftbsc.lll.proxies.MethodProxy; @@ -33,6 +33,7 @@ import static ftbsc.lll.processor.tools.ASTUtils.*; */ @SupportedAnnotationTypes("ftbsc.lll.processor.annotations.Patch") @SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedOptions("mappingsFile") public class LilleroProcessor extends AbstractProcessor { /** * A {@link Set} of {@link String}s that will contain the fully qualified names @@ -41,9 +42,35 @@ public class LilleroProcessor extends AbstractProcessor { private final Set generatedInjectors = new HashSet<>(); /** - * A static boolean that should be set to true when ran in a non-obfuscated environment. + * The {@link ObfuscationMapper} used to convert classes and variables + * to their obfuscated equivalent. Will be null when no mapper is in use. */ - public static boolean obfuscatedEnvironment = false; //todo: set this + private ObfuscationMapper mapper; + + /** + * Initializes the processor with the processing environment by + * setting the {@code processingEnv} field to the value of the + * {@code processingEnv} argument. + * @param processingEnv environment to access facilities the tool framework + * provides to the processor + * @throws IllegalStateException if this method is called more than once. + */ + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + String location = processingEnv.getOptions().get("mappingsFile"); + if(location == null) + mapper = null; + else { //TODO: add local file + try { + URL url = new URL(location); + InputStream is = url.openStream(); + mapper = new ObfuscationMapper(new BufferedReader(new InputStreamReader(is, + StandardCharsets.UTF_8)).lines()); + is.close(); + } catch(IOException ignored) {} //TODO: proper handling + } + } /** * Where the actual processing happens. @@ -106,7 +133,7 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the class name and maps it to the correct format. * @param name the fully qualified name of the class to convert - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @implNote De facto, there is never any difference between the SRG and MCP name of a class. * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) * names. However, this method still performs a conversion - just in case there is an @@ -114,15 +141,15 @@ public class LilleroProcessor extends AbstractProcessor { * @return the fully qualified class name * @since 0.3.0 */ - private static String findClassName(String name, SrgMapper mapper) { - return mapper == null ? name : mapper.mapClass(name, obfuscatedEnvironment).replace('/', '.'); + private static String findClassName(String name, ObfuscationMapper mapper) { + return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); } /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @implNote De facto, there is never any difference between the SRG and MCP name of a class. * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) * names. However, this method still performs a conversion - just in case there is an @@ -130,7 +157,7 @@ public class LilleroProcessor extends AbstractProcessor { * @return the fully qualified class name * @since 0.3.0 */ - private static String findClassName(Patch patchAnn, FindMethod methodAnn, SrgMapper mapper) { + private static String findClassName(Patch patchAnn, FindMethod methodAnn, ObfuscationMapper mapper) { String fullyQualifiedName = methodAnn == null || methodAnn.parent() == Object.class ? getClassFullyQualifiedName(patchAnn.value()) @@ -141,11 +168,11 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the class name and maps it to the correct format. * @param patchAnn the {@link Patch} annotation containing target class info - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findClassName(Patch patchAnn, SrgMapper mapper) { + private static String findClassName(Patch patchAnn, ObfuscationMapper mapper) { return findClassName(patchAnn, null, mapper); } @@ -153,12 +180,12 @@ public class LilleroProcessor extends AbstractProcessor { * Finds the member name and maps it to the correct format. * @param parentFQN the already mapped FQN of the parent class * @param memberName the name of the member - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findMemberName(String parentFQN, String memberName, SrgMapper mapper) { - return mapper == null ? memberName : mapper.mapMember(parentFQN, memberName, obfuscatedEnvironment); + private static String findMemberName(String parentFQN, String memberName, ObfuscationMapper mapper) { + return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName); } /** @@ -166,11 +193,11 @@ public class LilleroProcessor extends AbstractProcessor { * @param parentFQN the already mapped FQN of the parent class * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use, may be null + * @param mapper the {@link ObfuscationMapper} to use, may be null * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); try { methodName = findMemberName(parentFQN, methodName, mapper); @@ -188,11 +215,11 @@ public class LilleroProcessor extends AbstractProcessor { * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, SrgMapper mapper) { + private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { return findMethodName(findClassName(patchAnn, methodAnn, mapper), methodAnn, stub, mapper); } @@ -250,13 +277,13 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real method corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws AmbiguousDefinitionException if it finds more than one candidate * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private ExecutableElement findRealMethod(ExecutableElement stub, SrgMapper mapper) { + private ExecutableElement findRealMethod(ExecutableElement stub, ObfuscationMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindMethod findAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info Target target = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled @@ -272,12 +299,12 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real field corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link SrgMapper} to use + * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private VariableElement findField(ExecutableElement stub, SrgMapper mapper) { + private VariableElement findField(ExecutableElement stub, ObfuscationMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindField fieldAnn = stub.getAnnotation(FindField.class); String parentName = findClassName(getClassFullyQualifiedName( @@ -307,15 +334,6 @@ public class LilleroProcessor extends AbstractProcessor { * @param cl the {@link TypeElement} for the given class */ private void generateInjectors(TypeElement cl) { - SrgMapper mapper = null; - try { //TODO: cant we get it from local? - 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 ignored) {} //TODO: proper handling - //find class information Patch patchAnn = cl.getAnnotation(Patch.class); String targetClassSrgName = findClassName(patchAnn, mapper); @@ -481,7 +499,7 @@ public class LilleroProcessor extends AbstractProcessor { * @return a {@link List} of method specs * @since 0.2.0 */ - private List generateRequestedProxies(TypeElement cl, SrgMapper mapper) { + private List generateRequestedProxies(TypeElement cl, ObfuscationMapper mapper) { List generated = new ArrayList<>(); findAnnotatedMethods(cl, FindMethod.class) .stream() diff --git a/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java b/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java deleted file mode 100644 index d3c9f79..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/SrgMapper.java +++ /dev/null @@ -1,211 +0,0 @@ -package ftbsc.lll.processor.tools; - -import ftbsc.lll.exceptions.MappingNotFoundException; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; - -/** - * Parses a .tsrg file into a mapper capable of converting from - * deobfuscated names to SRG names. - * Obviously, it may only be used at runtime if the .tsrg file is - * included in the resources. However, in that case, I'd recommend - * using the built-in Forge one and refrain from including an extra - * resource for no good reason. - * @since 0.2.0 - */ -public class SrgMapper { - - /** - * A Map using the deobfuscated names as keys, - * holding information for that Srg class as value. - */ - private final Map mapper = new HashMap<>(); - - /** - * The public constructor. - * Should be passed a {@link Stream} of Strings, one representing each line. - * Whether they contain line endings or not is irrelevant. - * @param str a {@link Stream} of strings - */ - public SrgMapper(Stream str) { - AtomicReference currentClass = new AtomicReference<>(""); - str.forEach(l -> { - if(l.startsWith("\t")) - mapper.get(currentClass.get()).addMember(l); - else { - ObfuscationData s = new ObfuscationData(l); - currentClass.set(s.mcpName); - mapper.put(s.mcpName, s); - } - }); - } - - /** - * Gets the SRG-obfuscated name of the class. - * @param mcp the MCP (deobfuscated) internal name of the desired class - * @return the SRG name of the class - * @throws MappingNotFoundException if no mapping is found - */ - public String getSrgClass(String mcp) { - ObfuscationData data = mapper.get(mcp); - if(data == null) - throw new MappingNotFoundException(mcp); - else return data.srgName; - } - - /** - * Gets the MCP (deobfuscated) name of the class. - * Due to how it's implemented, it's considerably less efficient than its - * opposite operation. - * @param srg the SRG-obfuscated internal name of the desired class - * @return the MCP name of the class - */ - public String getMcpClass(String srg) { - ObfuscationData data = getObfuscationData(srg); - return data.mcpName; - } - - /** - * Gets one between the SRG and MCP names. - * @param name the internal name of the desired class in either format - * @param obf whether it should return the obfuscated name - * @return a {@link String} containing the internal name of the class - * @throws MappingNotFoundException if no mapping is found - * @since 0.3.0 - */ - public String mapClass(String name, boolean obf) { - String srg; - try { - srg = this.getSrgClass(name); - } catch(MappingNotFoundException e) { - srg = name; - name = this.getMcpClass(srg); - } - if(obf) return srg; - else return name; - } - - /** - * Gets the SRG-obfuscated name of a class member (field or method). - * The method signature must be in this format: "methodName methodDescriptor", - * with a space, because that's how it is in .tsrg files. - * @param mcpClass the MCP (deobfuscated) internal name of the container class - * @param member the field name or method signature - * @return the SRG name of the given member - * @throws MappingNotFoundException if no mapping is found - */ - public String getSrgMember(String mcpClass, String member) { - ObfuscationData data = mapper.get(mcpClass); - if(data == null) - throw new MappingNotFoundException(mcpClass + "::" + member); - return data.members.get(member); - } - - /** - * Gets the MCP (deobfuscated) name of the given member. - * Due to how it's implemented, it's considerably less efficient than its - * opposite operation. - * @param srgClass the SRG-obfuscated internal name of the container class - * @param member the field name or method signature - * @return the MCP name of the given member - */ - public String getMcpMember(String srgClass, String member) { - ObfuscationData data = getObfuscationData(srgClass); - for(String mcp : data.members.keySet()) - if(data.members.get(mcp).equals(member)) - return mcp; - return null; - } - - /** - * Obfuscates or deobfuscates a member, given one of its names and the effective. - * @param className the internal or fully qualified name of the container class - * @param memberName the member of the class - * @param obf whether it should return the obfuscated name - * @return the mapped member name - * @throws MappingNotFoundException if no mapping is found - * @since 0.3.0 - */ - public String mapMember(String className, String memberName, boolean obf) { - className = className.replace('.', '/'); - String effectiveClassName = this.mapClass(className, obf); - String srgMemberName; - try { - srgMemberName = this.getSrgMember(effectiveClassName, memberName); - } catch(MappingNotFoundException e) { - srgMemberName = memberName; - memberName = this.getMcpMember(effectiveClassName, memberName); - } - if(obf) return srgMemberName; - else return memberName; - } - - /** - * Used internally. Gets the obfuscation data corresponding to the given SRG name. - * @return the desired {@link ObfuscationData} object - * @throws MappingNotFoundException if no {@link ObfuscationData} object is found - */ - private ObfuscationData getObfuscationData(String srg) { - for(ObfuscationData s : mapper.values()) - if(s.srgName.equals(srg)) - return s; - throw new MappingNotFoundException(srg); - } - - /** - * Private class used internally for storing information about each - * class. It's private because there is no good reason anyone would - * want to access this outside of this class. - */ - private static class ObfuscationData { - /** - * The MCP internal name (FQN with '/' instad of '.') of the class. - */ - private final String mcpName; - - /** - * The SRG internal name (FQN with '/' instad of '.') of the class. - */ - private final String srgName; - - /** - * A {@link Map} tying each member's deobfuscated name or signature to its - * SRG name. - */ - private final Map members; - - - /** - * The constructor. It takes in the line where the class is declared, - * which looks something like this: - * {@code internal/name/mcp internal/name/srg } - * @param s the String represeting the declaration line - */ - private ObfuscationData(String s) { - String[] split = s.trim().split(" "); - this.mcpName = split[0]; - this.srgName = split[1]; - this.members = new HashMap<>(); - } - - /** - * Adds a member to the target class. It takes in the line where the - * member is declared. - * For fields it looks like this: - * {@code fieldMcpName field_srg_name} - * For methods it looks like this: - * {@code methodName methodDescriptor method_srg_name} - * @param s the String representing the declaration line - */ - public void addMember(String s) { - String[] split = s.trim().split(" "); - if(split.length == 2) //field - members.put(split[0], split[1]); - else if (split.length == 3) //method - members.put(split[0] + " " + split[1], split[2]); - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java new file mode 100644 index 0000000..1d19c6b --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -0,0 +1,167 @@ +package ftbsc.lll.processor.tools.obfuscation; + +import ftbsc.lll.exceptions.MappingNotFoundException; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +/** + * Parses a .tsrg file into a mapper capable of converting from + * deobfuscated names to obfuscated ones. + * Obviously, it may only be used at runtime if the .tsrg file is + * included in the resources. However, in that case, I'd recommend + * using the built-in Forge one and refrain from including an extra + * resource for no good reason. + * TODO: CSV format + * @since 0.2.0 + */ +public class ObfuscationMapper { + + /** + * A Map using the deobfuscated names as keys, + * holding information for that class as value. + */ + private final Map mapper = new HashMap<>(); + + /** + * The public constructor. + * Should be passed a {@link Stream} of Strings, one representing each line. + * Whether they contain line endings or not is irrelevant. + * @param str a {@link Stream} of strings + */ + public ObfuscationMapper(Stream str) { + AtomicReference currentClass = new AtomicReference<>(""); + str.forEach(l -> { + if(l.startsWith("\t")) + mapper.get(currentClass.get()).addMember(l); + else { + String[] sp = l.split(" "); + ObfuscationData s = new ObfuscationData(sp[0], sp[1]); + currentClass.set(s.unobf); + mapper.put(s.unobf, s); + } + }); + } + + /** + * Gets the obfuscated name of the class. + * @param name the unobfuscated internal name of the desired class + * @return the obfuscated name of the class + * @throws MappingNotFoundException if no mapping is found + */ + public String obfuscateClass(String name) { + ObfuscationData data = mapper.get(name); + if(data == null) + throw new MappingNotFoundException(name); + else return data.obf; + } + + /** + * Gets the unobfuscated name of the class. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param obfName the obfuscated internal name of the desired class + * @return the deobfuscated name of the class + */ + public String deobfuscateClass(String obfName) { + ObfuscationData data = getObfuscationData(obfName); + return data.unobf; + } + + /** + * Gets the obfuscated name of a class member (field or method). + * The method signature must be in this format: "methodName methodDescriptor", + * with a space, because that's how it is in .tsrg files. + * @param parentName the unobfuscated internal name of the parent class + * @param memberName the field name or method signature + * @return the obfuscated name of the given member + * @throws MappingNotFoundException if no mapping is found + */ + public String obfuscateMember(String parentName, String memberName) { + ObfuscationData data = mapper.get(parentName); + if(data == null) + throw new MappingNotFoundException(parentName + "::" + memberName); + return data.members.get(memberName); + } + + /** + * Gets the unobfuscated name of the given member. + * Due to how it's implemented, it's considerably less efficient than its + * opposite operation. + * @param parentObf the obfuscated internal name of the container class + * @param memberObf the field name or method signature + * @return the deobfuscated name of the given member + */ + public String deobfuscateMember(String parentObf, String memberObf) { + ObfuscationData data = getObfuscationData(parentObf); + for(String unobf : data.members.keySet()) + if(data.members.get(unobf).equals(memberObf)) + return unobf; + return null; + } + + /** + * Used internally. Gets the obfuscation data corresponding to the given obfuscated class name. + * @param classObfuscatedName the internal name of the obfuscated class + * @return the desired {@link ObfuscationData} object + * @throws MappingNotFoundException if no {@link ObfuscationData} object is found + */ + private ObfuscationData getObfuscationData(String classObfuscatedName) { + for(ObfuscationData s : mapper.values()) + if(s.obf.equals(classObfuscatedName)) + return s; + throw new MappingNotFoundException(classObfuscatedName); + } + + /** + * Private class used internally for storing information about each + * class. It's private because there is no good reason anyone would + * want to access this outside of this class. + */ + private static class ObfuscationData { + /** + * The unobfuscated name (FQN with '/' instad of '.') of the class. + */ + private final String unobf; + + /** + * The obfuscated internal name (FQN with '/' instad of '.') of the class. + */ + private final String obf; + + /** + * A {@link Map} tying each member's name or signature to its + * obfuscated counterpart. + */ + private final Map members; + + + /** + * The constructor. It takes in the names (obfuscated and non-obfuscated) + * of a class. + * @param unobf the unobfuscated name + * @param obf the obfuscated name + */ + private ObfuscationData(String unobf, String obf) { + this.unobf = unobf; + this.obf = obf; + this.members = new HashMap<>(); + } + + /** + * Adds a member to the target class. + * For fields only the names are required; for methods, + * this takes in the full signature ({@code name + " " + space}). + * @param s the String representing the declaration line + */ + public void addMember(String s) { + String[] split = s.trim().split(" "); + if(split.length == 2) //field + members.put(split[0], split[1]); + else if (split.length == 3) //method + members.put(split[0] + " " + split[1], split[2]); + } + } +} -- cgit v1.2.3-56-ga3b1 From 6e2ea19ab48987e01543b6db188f8e5ce3d1eeb9 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Mon, 13 Mar 2023 13:03:49 +0100 Subject: feat: may now pass local files as mappings --- .../lll/exceptions/InvalidResourceException.java | 22 ++++++++++++++++ .../lll/exceptions/MappingNotFoundException.java | 16 +++++++++--- .../java/ftbsc/lll/processor/LilleroProcessor.java | 29 ++++++++++++++++------ 3 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java new file mode 100644 index 0000000..dd75a08 --- /dev/null +++ b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java @@ -0,0 +1,22 @@ +package ftbsc.lll.exceptions; + +/** + * Thrown when a resource passed as an argument is not found. + */ +public class InvalidResourceException extends RuntimeException { + + /** + * Empty constructor, used when the provided resource exists but is empty. + */ + public InvalidResourceException() { + super("The specified resource was empty!"); + } + + /** + * Named constructor, used when the specified resource doesn't exist. + * @param name the resource name + */ + public InvalidResourceException(String name) { + super("Specified resource " + name + " was not found!"); + } +} diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java index a1a47d1..3334242 100644 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -1,17 +1,27 @@ package ftbsc.lll.exceptions; -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; +import ftbsc.lll.processor.tools.obfuscation.IMapper; /** - * Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}. + * Thrown upon failure to find the requested mapping within a loaded {@link IMapper}. */ public class MappingNotFoundException extends RuntimeException { /** * Constructs a new mapping not found exception for the specified mapping. - * @param mapping the detail message + * @param mapping the relevant mapping */ public MappingNotFoundException(String mapping) { super("Could not find mapping for " + mapping + "!"); } + + /** + * Constructs a new mapping not found exception for the specified mapping + * with the specified reason. + * @param mapping the relevant mapping + * @param reason the reason message + */ + public MappingNotFoundException(String mapping, String reason) { + this(mapping + ": " + reason); + } } diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index c6343a6..aaaa2f0 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -3,6 +3,7 @@ package ftbsc.lll.processor; import com.squareup.javapoet.*; import ftbsc.lll.IInjector; import ftbsc.lll.exceptions.AmbiguousDefinitionException; +import ftbsc.lll.exceptions.InvalidResourceException; import ftbsc.lll.exceptions.MappingNotFoundException; import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.processor.annotations.*; @@ -20,6 +21,8 @@ import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; @@ -61,14 +64,26 @@ public class LilleroProcessor extends AbstractProcessor { String location = processingEnv.getOptions().get("mappingsFile"); if(location == null) mapper = null; - else { //TODO: add local file + else { + InputStream targetStream; try { - URL url = new URL(location); - InputStream is = url.openStream(); - mapper = new ObfuscationMapper(new BufferedReader(new InputStreamReader(is, - StandardCharsets.UTF_8)).lines()); - is.close(); - } catch(IOException ignored) {} //TODO: proper handling + URI target = new URI(location); + targetStream = target.toURL().openStream(); + } catch(URISyntaxException | IOException e) { + //may be a local file path + File f = new File(location); + if(!f.exists()) + throw new InvalidResourceException(location); + try { + targetStream = new FileInputStream(f); + } catch(FileNotFoundException ex) { + throw new InvalidResourceException(location); + } + } + //assuming its tsrg file + //todo: replace crappy homebaked parser with actual library + this.mapper = new ObfuscationMapper(new BufferedReader(new InputStreamReader(targetStream, + StandardCharsets.UTF_8)).lines()); } } -- cgit v1.2.3-56-ga3b1 From 0cd50cc179bc9d901b87fb6fb9c4ba45e510a372 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 10:56:20 +0100 Subject: fix: oops didn't mean that --- src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java index 3334242..40b0b65 100644 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -1,9 +1,9 @@ package ftbsc.lll.exceptions; -import ftbsc.lll.processor.tools.obfuscation.IMapper; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; /** - * Thrown upon failure to find the requested mapping within a loaded {@link IMapper}. + * Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}. */ public class MappingNotFoundException extends RuntimeException { -- cgit v1.2.3-56-ga3b1 From 19b739943fabeed97d6d6a2bc4e70e7d832fea49 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 11:23:40 +0100 Subject: fix: fixed MirroredTypeException --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 15 ++++++++------- src/main/java/ftbsc/lll/processor/tools/ASTUtils.java | 8 +++++--- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index aaaa2f0..a2a42f2 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -29,6 +29,7 @@ import java.util.*; import java.util.stream.Collectors; import static ftbsc.lll.processor.tools.ASTUtils.*; +import static ftbsc.lll.processor.tools.ASTUtils.getClassFullyQualifiedName; /** * The actual annotation processor behind the magic. @@ -175,8 +176,8 @@ public class LilleroProcessor extends AbstractProcessor { private static String findClassName(Patch patchAnn, FindMethod methodAnn, ObfuscationMapper mapper) { String fullyQualifiedName = methodAnn == null || methodAnn.parent() == Object.class - ? getClassFullyQualifiedName(patchAnn.value()) - : getClassFullyQualifiedName(methodAnn.parent()); + ? getClassFullyQualifiedName(patchAnn, p -> patchAnn.value()) + : getClassFullyQualifiedName(methodAnn, m -> methodAnn.parent()); return findClassName(fullyQualifiedName, mapper); } @@ -322,11 +323,11 @@ public class LilleroProcessor extends AbstractProcessor { private VariableElement findField(ExecutableElement stub, ObfuscationMapper mapper) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindField fieldAnn = stub.getAnnotation(FindField.class); - String parentName = findClassName(getClassFullyQualifiedName( - fieldAnn.parent().equals(Object.class) - ? patchAnn.value() - : fieldAnn.parent() - ), mapper); + String parentName; + if(fieldAnn.parent().equals(Object.class)) + parentName = getClassFullyQualifiedName(patchAnn, p -> patchAnn.value()); + else parentName = getClassFullyQualifiedName(fieldAnn, f -> fieldAnn.parent()); + parentName = findClassName(parentName, mapper); String name = fieldAnn.name().equals("") ? stub.getSimpleName().toString() : fieldAnn.name(); diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index c35e008..fb6806e 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -13,6 +13,7 @@ import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.TypeMirror; import java.lang.annotation.Annotation; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -137,13 +138,14 @@ public class ASTUtils { * Safely converts a {@link Class} to its fully qualified name. See * this blogpost * for more information. - * @param clazz the class to get the name for + * @param ann the annotation containing the class + * @param fun the annotation function returning the class * @return the fully qualified name of the given class * @since 0.3.0 */ - public static String getClassFullyQualifiedName(Class clazz) { + public static String getClassFullyQualifiedName(T ann, Function> fun) { try { - return clazz.getCanonicalName(); + return fun.apply(ann).getCanonicalName(); } catch(MirroredTypeException e) { return e.getTypeMirror().toString(); } -- cgit v1.2.3-56-ga3b1 From c46106bebafeaf6d61a68577b6ebd65c00c836bf Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 11:41:42 +0100 Subject: fix: now the mapper automagically converts internal names to FQN --- .../ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java index 1d19c6b..12ae23f 100644 --- a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -52,7 +52,7 @@ public class ObfuscationMapper { * @throws MappingNotFoundException if no mapping is found */ public String obfuscateClass(String name) { - ObfuscationData data = mapper.get(name); + ObfuscationData data = mapper.get(name.replace('.', '/')); if(data == null) throw new MappingNotFoundException(name); else return data.obf; @@ -80,10 +80,13 @@ public class ObfuscationMapper { * @throws MappingNotFoundException if no mapping is found */ public String obfuscateMember(String parentName, String memberName) { - ObfuscationData data = mapper.get(parentName); + ObfuscationData data = mapper.get(parentName.replace('.', '/')); if(data == null) throw new MappingNotFoundException(parentName + "::" + memberName); - return data.members.get(memberName); + String member = data.members.get(memberName); + if(member == null) + throw new MappingNotFoundException(parentName + "::" + memberName); + return member; } /** -- cgit v1.2.3-56-ga3b1 From 38dd9ae869eabd561559964ac70cfb1ca54d9892 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 12:07:45 +0100 Subject: fix: repeatable annotation support fix --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 1 - src/main/java/ftbsc/lll/processor/tools/ASTUtils.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index a2a42f2..a872e3c 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -23,7 +23,6 @@ import javax.tools.StandardLocation; import java.io.*; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index fb6806e..8361367 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -31,7 +31,7 @@ public class ASTUtils { public static List findAnnotatedMethods(TypeElement cl, Class ann) { return cl.getEnclosedElements() .stream() - .filter(e -> e.getAnnotation(ann) != null) + .filter(e -> e.getAnnotationsByType(ann).length != 0) .map(e -> (ExecutableElement) e) .collect(Collectors.toList()); } -- cgit v1.2.3-56-ga3b1 From 8fd7006a346e562ed11bdb3c0cfebba3197ff860 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 12:25:59 +0100 Subject: fix: repeatable annotation support fix, part 2 --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index a872e3c..f6ef045 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -360,7 +360,7 @@ public class LilleroProcessor extends AbstractProcessor { String packageName = packageElement.toString(); //find injector(s) and target(s) - List injectors = findAnnotatedMethods(cl, MultipleInjectors.class); + List injectors = findAnnotatedMethods(cl, Injector.class); List targets = findAnnotatedMethods(cl, Target.class); @@ -375,9 +375,9 @@ public class LilleroProcessor extends AbstractProcessor { Map toGenerate = new HashMap<>(); for(ExecutableElement inj : injectors) { - MultipleInjectors minjAnn = inj.getAnnotation(MultipleInjectors.class); + Injector[] minjAnn = inj.getAnnotationsByType(Injector.class); int iterationNumber = 1; - for(Injector injectorAnn : minjAnn.value()) { //java is dumb + for(Injector injectorAnn : minjAnn) { //java is dumb List injectionCandidates = targets; //case 1: it has a name, try to match it -- cgit v1.2.3-56-ga3b1 From 2463dd3e762391d5a35fd05269e63275e80b5a1d Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 12:59:54 +0100 Subject: fix: fixed MirroredTypesException --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index f6ef045..177e12f 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -15,6 +15,7 @@ import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.MirroredTypesException; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.FileObject; @@ -407,9 +408,19 @@ public class LilleroProcessor extends AbstractProcessor { if(injectionCandidates.size() == 1) injectionTarget = injectionCandidates.get(0); - if(injectorAnn.params().length != 0) { + List params = new ArrayList<>(); + try { + params.addAll(Arrays.stream(injectorAnn.params()) + .map(Class::getCanonicalName) + .map(fqn -> processingEnv.getElementUtils().getTypeElement(fqn).asType()) + .collect(Collectors.toList())); + } catch(MirroredTypesException e) { + params.addAll(e.getTypeMirrors()); + } + + if(params.size() != 0) { StringBuilder descr = new StringBuilder("("); - for(Class p : injectorAnn.params()) + for(TypeMirror p : params) descr.append(descriptorFromType(TypeName.get(p))); descr.append(")"); injectionCandidates = -- cgit v1.2.3-56-ga3b1 From 92a4273b10759494e38eee2408f99ea958a1767a Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 15:10:16 +0100 Subject: fix: fixed mappings bug --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 35 +++++++--------- .../java/ftbsc/lll/processor/tools/ASTUtils.java | 47 ++++++++++++++++++++-- .../tools/obfuscation/ObfuscationMapper.java | 35 +++++++++++++++- 3 files changed, 91 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 177e12f..491ce68 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -15,7 +15,6 @@ import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.ExecutableType; -import javax.lang.model.type.MirroredTypesException; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.FileObject; @@ -176,8 +175,8 @@ public class LilleroProcessor extends AbstractProcessor { private static String findClassName(Patch patchAnn, FindMethod methodAnn, ObfuscationMapper mapper) { String fullyQualifiedName = methodAnn == null || methodAnn.parent() == Object.class - ? getClassFullyQualifiedName(patchAnn, p -> patchAnn.value()) - : getClassFullyQualifiedName(methodAnn, m -> methodAnn.parent()); + ? getClassFullyQualifiedName(patchAnn, Patch::value) + : getClassFullyQualifiedName(methodAnn, FindMethod::parent); return findClassName(fullyQualifiedName, mapper); } @@ -200,8 +199,8 @@ public class LilleroProcessor extends AbstractProcessor { * @return the internal class name * @since 0.3.0 */ - private static String findMemberName(String parentFQN, String memberName, ObfuscationMapper mapper) { - return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName); + private static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { + return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); } /** @@ -213,14 +212,18 @@ public class LilleroProcessor extends AbstractProcessor { * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { + private String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); + String methodDescriptor; + if(methodAnn == null) + methodDescriptor = descriptorFromExecutableElement(stub); + else methodDescriptor = methodDescriptorFromParams(methodAnn, FindMethod::params, processingEnv.getElementUtils()); try { - methodName = findMemberName(parentFQN, methodName, mapper); + methodName = findMemberName(parentFQN, methodName, methodDescriptor, mapper); } catch(MappingNotFoundException e) { //not found: try again with the name of the annotated method if(methodAnn == null) { - methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), mapper); + methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), methodDescriptor, mapper); } else throw e; } return methodName; @@ -235,7 +238,7 @@ public class LilleroProcessor extends AbstractProcessor { * @return the internal class name * @since 0.3.0 */ - private static String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { + private String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { return findMethodName(findClassName(patchAnn, methodAnn, mapper), methodAnn, stub, mapper); } @@ -325,8 +328,8 @@ public class LilleroProcessor extends AbstractProcessor { FindField fieldAnn = stub.getAnnotation(FindField.class); String parentName; if(fieldAnn.parent().equals(Object.class)) - parentName = getClassFullyQualifiedName(patchAnn, p -> patchAnn.value()); - else parentName = getClassFullyQualifiedName(fieldAnn, f -> fieldAnn.parent()); + parentName = getClassFullyQualifiedName(patchAnn, Patch::value); + else parentName = getClassFullyQualifiedName(fieldAnn, FindField::parent); parentName = findClassName(parentName, mapper); String name = fieldAnn.name().equals("") ? stub.getSimpleName().toString() @@ -408,15 +411,7 @@ public class LilleroProcessor extends AbstractProcessor { if(injectionCandidates.size() == 1) injectionTarget = injectionCandidates.get(0); - List params = new ArrayList<>(); - try { - params.addAll(Arrays.stream(injectorAnn.params()) - .map(Class::getCanonicalName) - .map(fqn -> processingEnv.getElementUtils().getTypeElement(fqn).asType()) - .collect(Collectors.toList())); - } catch(MirroredTypesException e) { - params.addAll(e.getTypeMirrors()); - } + List params = classArrayFromAnnotation(injectorAnn, Injector::params, processingEnv.getElementUtils()); if(params.size() != 0) { StringBuilder descr = new StringBuilder("("); diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index 8361367..faac3dc 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -6,12 +6,17 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import ftbsc.lll.tools.DescriptorBuilder; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.MirroredTypesException; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @@ -135,19 +140,53 @@ public class ASTUtils { } /** - * Safely converts a {@link Class} to its fully qualified name. See - * this blogpost - * for more information. + * Safely extracts a {@link Class} from an annotation and gets its fully qualified name. * @param ann the annotation containing the class * @param fun the annotation function returning the class * @return the fully qualified name of the given class * @since 0.3.0 */ - public static String getClassFullyQualifiedName(T ann, Function> fun) { + public static String getClassFullyQualifiedName(T ann, Function> fun) { try { return fun.apply(ann).getCanonicalName(); } catch(MirroredTypeException e) { return e.getTypeMirror().toString(); } } + + /** + * Safely extracts a {@link Class} array from an annotation. + * @param ann the annotation containing the class + * @param fun the annotation function returning the class + * @param elementUtils the element utils corresponding to the {@link ProcessingEnvironment} + * @return a list of {@link TypeMirror}s representing the classes + * @since 0.3.0 + */ + public static List classArrayFromAnnotation(T ann, Function[]> fun, Elements elementUtils) { + List params = new ArrayList<>(); + try { + params.addAll(Arrays.stream(fun.apply(ann)) + .map(Class::getCanonicalName) + .map(fqn -> elementUtils.getTypeElement(fqn).asType()) + .collect(Collectors.toList())); + } catch(MirroredTypesException e) { + params.addAll(e.getTypeMirrors()); + } + return params; + } + + /** + * Builds a (partial, not including the return type) method descriptor from its parameters + * @param ann the annotation containing the class + * @param fun the annotation function returning the class + * @return the method descriptor + */ + public static String methodDescriptorFromParams(T ann, Function[]> fun, Elements elementUtils) { + List mirrors = classArrayFromAnnotation(ann, fun, elementUtils); + StringBuilder sb = new StringBuilder("("); + for(TypeMirror t : mirrors) + sb.append(descriptorFromType(t)); + sb.append(")"); + return sb.toString(); + } } diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java index 12ae23f..5e54775 100644 --- a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -1,10 +1,13 @@ package ftbsc.lll.processor.tools.obfuscation; +import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.MappingNotFoundException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -76,14 +79,15 @@ public class ObfuscationMapper { * with a space, because that's how it is in .tsrg files. * @param parentName the unobfuscated internal name of the parent class * @param memberName the field name or method signature + * @param methodDescriptor the optional descriptor of the member, may be null or partial * @return the obfuscated name of the given member * @throws MappingNotFoundException if no mapping is found */ - public String obfuscateMember(String parentName, String memberName) { + public String obfuscateMember(String parentName, String memberName, String methodDescriptor) { ObfuscationData data = mapper.get(parentName.replace('.', '/')); if(data == null) throw new MappingNotFoundException(parentName + "::" + memberName); - String member = data.members.get(memberName); + String member = data.get(memberName, methodDescriptor); if(member == null) throw new MappingNotFoundException(parentName + "::" + memberName); return member; @@ -166,5 +170,32 @@ public class ObfuscationMapper { else if (split.length == 3) //method members.put(split[0] + " " + split[1], split[2]); } + + /** + * Gets an obfuscated member given the method name and a method descriptor, + * which may be partial (i.e. not include return type) or null if the member + * is not a method. + * @param memberName member name + * @param methodDescriptor the method descriptor, or null if it's not a method + * @return the requested obfuscated name, or null if nothing was found + * @throws AmbiguousDefinitionException if not enough data was given to uniquely identify a mapping + */ + public String get(String memberName, String methodDescriptor) { + if(methodDescriptor == null) + return members.get(memberName); + List candidates = members.values().stream().filter(m -> m.startsWith(memberName)).collect(Collectors.toList()); + if(candidates.size() == 1) + return members.get(candidates.get(0)); + String signature = memberName + " " + methodDescriptor; + candidates = candidates.stream().filter(m -> m.startsWith(signature)).collect(Collectors.toList()); + switch(candidates.size()) { + case 0: + return null; + case 1: + return members.get(candidates.get(0)); + default: + throw new AmbiguousDefinitionException("Mapper could not uniquely identify method " + unobf + "::" + memberName); + } + } } } -- cgit v1.2.3-56-ga3b1 From 309415056e2c18c521f3da69a9623e1e47b9f422 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 15:14:15 +0100 Subject: fix: logic error in main loop --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 70 ++++++++++------------ 1 file changed, 33 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 491ce68..aa69cc2 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -384,64 +384,60 @@ public class LilleroProcessor extends AbstractProcessor { for(Injector injectorAnn : minjAnn) { //java is dumb List injectionCandidates = targets; - //case 1: it has a name, try to match it - if(!injectorAnn.targetName().equals("") && targetNames.contains(injectorAnn.targetName())) + if(!injectorAnn.targetName().equals("") && targetNames.contains(injectorAnn.targetName())) { + //case 1: it has a name, try to match it injectionCandidates = injectionCandidates .stream() .filter(i -> i.getSimpleName().toString().equals(injectorAnn.targetName())) .collect(Collectors.toList()); - - //case 2: try to match by injectTargetName - String inferredName = inj.getSimpleName() - .toString() - .replaceFirst("inject", ""); - injectionCandidates = - injectionCandidates - .stream() - .filter(t -> t.getSimpleName().toString().equalsIgnoreCase(inferredName)) - .collect(Collectors.toList()); - - //case 3: there is only one target - if(targets.size() == 1) + } else if(targets.size() == 1) { + //case 2: there is only one target injectionCandidates.add(targets.get(0)); + } else { + //case 3: try to match by injectTargetName + String inferredName = inj.getSimpleName() + .toString() + .replaceFirst("inject", ""); + injectionCandidates = + injectionCandidates + .stream() + .filter(t -> t.getSimpleName().toString().equalsIgnoreCase(inferredName)) + .collect(Collectors.toList()); + } ExecutableElement injectionTarget = null; if(injectionCandidates.size() == 1) injectionTarget = injectionCandidates.get(0); - - List params = classArrayFromAnnotation(injectorAnn, Injector::params, processingEnv.getElementUtils()); - - if(params.size() != 0) { - StringBuilder descr = new StringBuilder("("); - for(TypeMirror p : params) - descr.append(descriptorFromType(TypeName.get(p))); - descr.append(")"); - injectionCandidates = - injectionCandidates - .stream() - .filter(t -> //we care about arguments but not really about return type + else { + List params = classArrayFromAnnotation(injectorAnn, Injector::params, processingEnv.getElementUtils()); + + if(params.size() != 0) { + StringBuilder descr = new StringBuilder("("); + for(TypeMirror p : params) + descr.append(descriptorFromType(TypeName.get(p))); + descr.append(")"); + injectionCandidates = + injectionCandidates + .stream() + .filter(t -> //we care about arguments but not really about return type descr.toString() .split("\\)")[0] .equalsIgnoreCase(descriptorFromExecutableElement(t).split("\\)")[0]) - ).collect(Collectors.toList()); - } + ).collect(Collectors.toList()); + } - if(injectionCandidates.size() == 1) - injectionTarget = injectionCandidates.get(0); + if(injectionCandidates.size() == 1) + injectionTarget = injectionCandidates.get(0); + } //if we haven't found it yet, it's an ambiguity if(injectionTarget == null) throw new AmbiguousDefinitionException("Unclear target for injector " + inj.getSimpleName().toString() + "!"); else toGenerate.put( cl.getSimpleName().toString() + "Injector" + iterationNumber, - new InjectorInfo( - inj, findRealMethod( - injectionTarget, - mapper - ) - ) + new InjectorInfo(inj, findRealMethod(injectionTarget, mapper)) ); iterationNumber++; } -- cgit v1.2.3-56-ga3b1 From f54854a42fb7a6db0e4ab4267a360a038180a339 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 15:32:56 +0100 Subject: chore: replaced string concats with String.format, fixed logic in main loop --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 26 ++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index aa69cc2..5465bc0 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -140,7 +140,7 @@ public class LilleroProcessor extends AbstractProcessor { })) return true; else { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, - "Missing valid @Injector method in @Patch class " + elem + ", skipping."); + String.format("Missing valid @Injector method in @Patch class %s, skipping.", elem)); return false; } } @@ -256,7 +256,7 @@ public class LilleroProcessor extends AbstractProcessor { private ExecutableElement findMethod(String fullyQualifiedNameParent, String name, String descr, boolean strict) { TypeElement parent = processingEnv.getElementUtils().getTypeElement(fullyQualifiedNameParent); if(parent == null) - throw new AmbiguousDefinitionException("Could not find parent class " + fullyQualifiedNameParent + "!"); + throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", fullyQualifiedNameParent)); //try to find by name List candidates = parent.getEnclosedElements() @@ -266,14 +266,12 @@ public class LilleroProcessor extends AbstractProcessor { .filter(e -> e.getSimpleName().contentEquals(name)) .collect(Collectors.toList()); if(candidates.size() == 0) - throw new TargetNotFoundException(name + " " + descr); + throw new TargetNotFoundException(String.format("%s %s", name, descr)); if(candidates.size() == 1 && !strict) return candidates.get(0); if(descr == null) { throw new AmbiguousDefinitionException( - "Found " + candidates.size() - + " methods named " + name - + " in class " + fullyQualifiedNameParent + "!" + String.format("Found %d methods named %s in class %s!", candidates.size(), name, fullyQualifiedNameParent) ); } else { candidates = candidates.stream() @@ -282,12 +280,10 @@ public class LilleroProcessor extends AbstractProcessor { : c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0]) ).collect(Collectors.toList()); if(candidates.size() == 0) - throw new TargetNotFoundException(name + " " + descr); + throw new TargetNotFoundException(String.format("%s %s", name, descr)); if(candidates.size() > 1) throw new AmbiguousDefinitionException( - "Found " + candidates.size() - + " methods named " + name - + " in class " + fullyQualifiedNameParent + "!" + String.format("Found %d methods named %s in class %s!", candidates.size(), name, fullyQualifiedNameParent) ); return candidates.get(0); } @@ -393,6 +389,7 @@ public class LilleroProcessor extends AbstractProcessor { .collect(Collectors.toList()); } else if(targets.size() == 1) { //case 2: there is only one target + injectionCandidates = new ArrayList<>(); injectionCandidates.add(targets.get(0)); } else { //case 3: try to match by injectTargetName @@ -410,6 +407,7 @@ public class LilleroProcessor extends AbstractProcessor { if(injectionCandidates.size() == 1) injectionTarget = injectionCandidates.get(0); + else { List params = classArrayFromAnnotation(injectorAnn, Injector::params, processingEnv.getElementUtils()); @@ -434,9 +432,9 @@ public class LilleroProcessor extends AbstractProcessor { //if we haven't found it yet, it's an ambiguity if(injectionTarget == null) - throw new AmbiguousDefinitionException("Unclear target for injector " + inj.getSimpleName().toString() + "!"); + throw new AmbiguousDefinitionException(String.format("Unclear target for injector %s::%s!", cl.getSimpleName(), inj.getSimpleName())); else toGenerate.put( - cl.getSimpleName().toString() + "Injector" + iterationNumber, + String.format("%sInjector%d", cl.getSimpleName(), iterationNumber), new InjectorInfo(inj, findRealMethod(injectionTarget, mapper)) ); iterationNumber++; @@ -461,7 +459,7 @@ public class LilleroProcessor extends AbstractProcessor { TypeName.get(processingEnv .getElementUtils() .getTypeElement("org.objectweb.asm.tree.MethodNode").asType()), "main").build()) - .addStatement("super." + toGenerate.get(injName).injector.getSimpleName() + "(clazz, main)", TypeName.get(cl.asType())) + .addStatement(String.format("super.%s(clazz, main)", toGenerate.get(injName).injector.getSimpleName()), TypeName.get(cl.asType())) .build(); TypeSpec injectorClass = TypeSpec.classBuilder(injName) @@ -479,7 +477,7 @@ public class LilleroProcessor extends AbstractProcessor { .build(); JavaFile javaFile = JavaFile.builder(packageName, injectorClass).build(); - String injectorClassName = packageName + "." + injName; + String injectorClassName = String.format("%s.%s", packageName, injName); try { JavaFileObject injectorFile = processingEnv.getFiler().createSourceFile(injectorClassName); -- cgit v1.2.3-56-ga3b1 From f4b43e77beb5f39f3043c451d0b36467a590e06c Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 16:27:50 +0100 Subject: fix: now only obfuscate name after finding real method --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 83 ++++++++++------------ 1 file changed, 39 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 5465bc0..aaa1e5a 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -149,10 +149,6 @@ public class LilleroProcessor extends AbstractProcessor { * Finds the class name and maps it to the correct format. * @param name the fully qualified name of the class to convert * @param mapper the {@link ObfuscationMapper} to use, may be null - * @implNote De facto, there is never any difference between the SRG and MCP name of a class. - * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) - * names. However, this method still performs a conversion - just in case there is an - * odd one out. * @return the fully qualified class name * @since 0.3.0 */ @@ -165,10 +161,6 @@ public class LilleroProcessor extends AbstractProcessor { * @param patchAnn the {@link Patch} annotation containing target class info * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null * @param mapper the {@link ObfuscationMapper} to use, may be null - * @implNote De facto, there is never any difference between the SRG and MCP name of a class. - * In theory, differences only arise between SRG/MCP names and Notch (fully obfuscated) - * names. However, this method still performs a conversion - just in case there is an - * odd one out. * @return the fully qualified class name * @since 0.3.0 */ @@ -205,43 +197,30 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the method name and maps it to the correct format. + * * @param parentFQN the already mapped FQN of the parent class * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link ObfuscationMapper} to use, may be null + * @param stub the {@link ExecutableElement} for the stub * @return the internal class name * @since 0.3.0 */ - private String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { + private String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub) { String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); String methodDescriptor; if(methodAnn == null) methodDescriptor = descriptorFromExecutableElement(stub); else methodDescriptor = methodDescriptorFromParams(methodAnn, FindMethod::params, processingEnv.getElementUtils()); try { - methodName = findMemberName(parentFQN, methodName, methodDescriptor, mapper); + methodName = findMemberName(parentFQN, methodName, methodDescriptor, null); } catch(MappingNotFoundException e) { //not found: try again with the name of the annotated method if(methodAnn == null) { - methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), methodDescriptor, mapper); + methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), methodDescriptor, null); } else throw e; } return methodName; } - /** - * Finds the method name and maps it to the correct format. - * @param patchAnn the {@link Patch} annotation containing target class info - * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link ObfuscationMapper} to use - * @return the internal class name - * @since 0.3.0 - */ - private String findMethodName(Patch patchAnn, FindMethod methodAnn, ExecutableElement stub, ObfuscationMapper mapper) { - return findMethodName(findClassName(patchAnn, methodAnn, mapper), methodAnn, stub, mapper); - } - /** * Finds a method given name, container and descriptor. * @param fullyQualifiedNameParent the fully qualified name of the parent class of the method @@ -292,18 +271,17 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real method corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws AmbiguousDefinitionException if it finds more than one candidate * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private ExecutableElement findRealMethod(ExecutableElement stub, ObfuscationMapper mapper) { + private ExecutableElement findRealMethodFromStub(ExecutableElement stub) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindMethod findAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info Target target = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled - String parentFQN = findClassName(patchAnn, findAnn, mapper); - String methodName = findMethodName(patchAnn, findAnn, stub, mapper); + String parentFQN = findClassName(patchAnn, findAnn, null); + String methodName = findMethodName(findClassName(patchAnn, findAnn, null), findAnn, stub); return findMethod( parentFQN, methodName, @@ -314,19 +292,18 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the real field corresponding to a stub. * @param stub the {@link ExecutableElement} for the stub - * @param mapper the {@link ObfuscationMapper} to use * @return the desired method, if it exists * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private VariableElement findField(ExecutableElement stub, ObfuscationMapper mapper) { + private VariableElement findField(ExecutableElement stub) { Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); FindField fieldAnn = stub.getAnnotation(FindField.class); String parentName; if(fieldAnn.parent().equals(Object.class)) parentName = getClassFullyQualifiedName(patchAnn, Patch::value); else parentName = getClassFullyQualifiedName(fieldAnn, FindField::parent); - parentName = findClassName(parentName, mapper); + parentName = findClassName(parentName, null); String name = fieldAnn.name().equals("") ? stub.getSimpleName().toString() : fieldAnn.name(); @@ -351,7 +328,7 @@ public class LilleroProcessor extends AbstractProcessor { private void generateInjectors(TypeElement cl) { //find class information Patch patchAnn = cl.getAnnotation(Patch.class); - String targetClassSrgName = findClassName(patchAnn, mapper); + String targetClassFQN = findClassName(patchAnn, mapper).replace('/', '.'); //find package information Element packageElement = cl.getEnclosingElement(); @@ -435,7 +412,7 @@ public class LilleroProcessor extends AbstractProcessor { throw new AmbiguousDefinitionException(String.format("Unclear target for injector %s::%s!", cl.getSimpleName(), inj.getSimpleName())); else toGenerate.put( String.format("%sInjector%d", cl.getSimpleName(), iterationNumber), - new InjectorInfo(inj, findRealMethod(injectionTarget, mapper)) + new InjectorInfo(inj, findRealMethodFromStub(injectionTarget)) ); iterationNumber++; } @@ -443,6 +420,9 @@ public class LilleroProcessor extends AbstractProcessor { //iterate over the map and generate the classes for(String injName : toGenerate.keySet()) { + String targetMethodDescriptor = descriptorFromExecutableElement(toGenerate.get(injName).target); + String targetMethodName = findMemberName(targetClassFQN, toGenerate.get(injName).target.getSimpleName().toString(), targetMethodDescriptor, this.mapper); + MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).target) .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") .build(); @@ -468,10 +448,10 @@ public class LilleroProcessor extends AbstractProcessor { .addSuperinterface(ClassName.get(IInjector.class)) .addMethod(buildStringReturnMethod("name", cl.getSimpleName().toString())) .addMethod(buildStringReturnMethod("reason", patchAnn.reason())) - .addMethod(buildStringReturnMethod("targetClass", targetClassSrgName.replace('/', '.'))) - .addMethod(buildStringReturnMethod("methodName", toGenerate.get(injName).target.getSimpleName().toString())) - .addMethod(buildStringReturnMethod("methodDesc", descriptorFromExecutableElement(toGenerate.get(injName).target))) - .addMethods(generateRequestedProxies(cl, mapper)) + .addMethod(buildStringReturnMethod("targetClass", targetClassFQN)) + .addMethod(buildStringReturnMethod("methodName", targetMethodName)) + .addMethod(buildStringReturnMethod("methodDesc", targetMethodDescriptor)) + .addMethods(generateRequestedProxies(cl, this.mapper)) .addMethod(stubOverride) .addMethod(inject) .build(); @@ -521,20 +501,28 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { - ExecutableElement targetMethod = findRealMethod(m, mapper); + ExecutableElement targetMethod = findRealMethodFromStub(m); MethodSpec.Builder b = MethodSpec.overriding(m); + + String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper); + b.addStatement("$T bd = $T.builder($S)", MethodProxy.Builder.class, MethodProxy.class, - m.getSimpleName().toString() + findMemberName(targetParentFQN, targetMethod.getSimpleName().toString(), descriptorFromExecutableElement(targetMethod), mapper) ); - b.addStatement("bd.setParent($S)", ((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString()); + + b.addStatement("bd.setParent($S)", targetParentFQN); + for(Modifier mod : targetMethod.getModifiers()) b.addStatement("bd.addModifier($L)", mapModifier(mod)); + for(TypeParameterElement p : targetMethod.getTypeParameters()) b.addStatement("bd.addParameter($T.class)", p.asType()); + b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); b.addStatement("return bd.build()"); + generated.add(b.build()); }); findAnnotatedMethods(cl, FindField.class) @@ -542,18 +530,25 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { - VariableElement targetField = findField(m, mapper); + VariableElement targetField = findField(m); MethodSpec.Builder b = MethodSpec.overriding(m); + + String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper); + b.addStatement("$T bd = $T.builder($S)", FieldProxy.Builder.class, FieldProxy.class, - targetField.getSimpleName().toString() + findMemberName(targetParentFQN, targetField.getSimpleName().toString(), null, mapper) ); + b.addStatement("bd.setParent($S)", ((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString()); + for(Modifier mod : targetField.getModifiers()) + b.addStatement("bd.addModifier($L)", mapModifier(mod)); b.addStatement("bd.setType($T.class)", targetField.asType()); b.addStatement("return bd.build()"); + generated.add(b.build()); }); return generated; -- cgit v1.2.3-56-ga3b1 From d93f9388e80af18ef1729c1ff47eaa7f88bf00fc Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 19:44:36 +0100 Subject: fix: override packageprivate, minor fixes in ObfuscationMapper --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 48 +++++++++++++++++++--- .../ftbsc/lll/processor/annotations/Target.java | 2 +- .../tools/obfuscation/ObfuscationMapper.java | 2 +- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index aaa1e5a..1e3bf40 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -16,6 +16,7 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.JavaFileObject; @@ -153,7 +154,11 @@ public class LilleroProcessor extends AbstractProcessor { * @since 0.3.0 */ private static String findClassName(String name, ObfuscationMapper mapper) { - return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); + try { + return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); + } catch(MappingNotFoundException e) { + return name; + } } /** @@ -192,7 +197,11 @@ public class LilleroProcessor extends AbstractProcessor { * @since 0.3.0 */ private static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { - return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); + try { + return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); + } catch(MappingNotFoundException e) { + return memberName; + } } /** @@ -423,7 +432,7 @@ public class LilleroProcessor extends AbstractProcessor { String targetMethodDescriptor = descriptorFromExecutableElement(toGenerate.get(injName).target); String targetMethodName = findMemberName(targetClassFQN, toGenerate.get(injName).target.getSimpleName().toString(), targetMethodDescriptor, this.mapper); - MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).target) + MethodSpec stubOverride = unsafeOverriding(toGenerate.get(injName).target) .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") .build(); @@ -487,6 +496,35 @@ public class LilleroProcessor extends AbstractProcessor { .build(); } + /** + * Identical to {@link MethodSpec#overriding(ExecutableElement)}, but does not check for + * the accessor level of the target method. May be used to override package-private methods + * that are technically classified as private, but may be accessed under certain conditions. + * @param target the method to override + * @return the built method spec + * @since 0.3.0 + * @see MethodSpec#overriding(ExecutableElement) + */ + private static MethodSpec.Builder unsafeOverriding(ExecutableElement target) { + MethodSpec.Builder bd = + MethodSpec.methodBuilder(target.getSimpleName().toString()) + .addAnnotation(Override.class) + .addModifiers(target.getModifiers()) + .returns(TypeName.get(target.getReturnType())); + bd.modifiers.remove(Modifier.DEFAULT); + bd.modifiers.remove(Modifier.ABSTRACT); + for (TypeParameterElement typeParameterElement : target.getTypeParameters()) { + TypeVariable var = (TypeVariable) typeParameterElement.asType(); + bd.addTypeVariable(TypeVariableName.get(var)); + } + for(VariableElement p : target.getParameters()) + bd.addParameter(TypeName.get(p.asType()), p.getSimpleName().toString(), p.getModifiers().toArray(new Modifier[0])); + bd.varargs(target.isVarArgs()); + for(TypeMirror thrownType : target.getThrownTypes()) + bd.addException(TypeName.get(thrownType)); + return bd; + } + /** * Finds any method annotated with {@link FindMethod} or {@link FindField} within the given * class, and builds the {@link MethodSpec} necessary for building it. @@ -502,7 +540,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { ExecutableElement targetMethod = findRealMethodFromStub(m); - MethodSpec.Builder b = MethodSpec.overriding(m); + MethodSpec.Builder b = unsafeOverriding(m); String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -531,7 +569,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { VariableElement targetField = findField(m); - MethodSpec.Builder b = MethodSpec.overriding(m); + MethodSpec.Builder b = unsafeOverriding(m); String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper); diff --git a/src/main/java/ftbsc/lll/processor/annotations/Target.java b/src/main/java/ftbsc/lll/processor/annotations/Target.java index 38477d1..99b8552 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/Target.java +++ b/src/main/java/ftbsc/lll/processor/annotations/Target.java @@ -21,7 +21,7 @@ public @interface Target { * When set to false, tells the processor to first try to match a single method by name, * and to only check parameters if further clarification is needed. * @implNote While non-strict mode is more computationally efficient, it's ultimately not - * relevant, as it only matters at compile time. Do not set this to true unless + * relevant, as it only matters at compile time. Do not set this to false unless * you are sure know what you're doing. * @since 0.3.0 */ diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java index 5e54775..6da8c9b 100644 --- a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -183,7 +183,7 @@ public class ObfuscationMapper { public String get(String memberName, String methodDescriptor) { if(methodDescriptor == null) return members.get(memberName); - List candidates = members.values().stream().filter(m -> m.startsWith(memberName)).collect(Collectors.toList()); + List candidates = members.keySet().stream().filter(m -> m.startsWith(memberName)).collect(Collectors.toList()); if(candidates.size() == 1) return members.get(candidates.get(0)); String signature = memberName + " " + methodDescriptor; -- cgit v1.2.3-56-ga3b1 From a425ce62f9c9db49dde81c3bc437451c69f0a363 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 20:07:27 +0100 Subject: fix: removed unsafeOverride, i had a brainfart --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 37 +++------------------- 1 file changed, 4 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 1e3bf40..2040f20 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -421,7 +421,7 @@ public class LilleroProcessor extends AbstractProcessor { throw new AmbiguousDefinitionException(String.format("Unclear target for injector %s::%s!", cl.getSimpleName(), inj.getSimpleName())); else toGenerate.put( String.format("%sInjector%d", cl.getSimpleName(), iterationNumber), - new InjectorInfo(inj, findRealMethodFromStub(injectionTarget)) + new InjectorInfo(inj, injectionTarget) ); iterationNumber++; } @@ -432,7 +432,7 @@ public class LilleroProcessor extends AbstractProcessor { String targetMethodDescriptor = descriptorFromExecutableElement(toGenerate.get(injName).target); String targetMethodName = findMemberName(targetClassFQN, toGenerate.get(injName).target.getSimpleName().toString(), targetMethodDescriptor, this.mapper); - MethodSpec stubOverride = unsafeOverriding(toGenerate.get(injName).target) + MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).target) .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") .build(); @@ -496,35 +496,6 @@ public class LilleroProcessor extends AbstractProcessor { .build(); } - /** - * Identical to {@link MethodSpec#overriding(ExecutableElement)}, but does not check for - * the accessor level of the target method. May be used to override package-private methods - * that are technically classified as private, but may be accessed under certain conditions. - * @param target the method to override - * @return the built method spec - * @since 0.3.0 - * @see MethodSpec#overriding(ExecutableElement) - */ - private static MethodSpec.Builder unsafeOverriding(ExecutableElement target) { - MethodSpec.Builder bd = - MethodSpec.methodBuilder(target.getSimpleName().toString()) - .addAnnotation(Override.class) - .addModifiers(target.getModifiers()) - .returns(TypeName.get(target.getReturnType())); - bd.modifiers.remove(Modifier.DEFAULT); - bd.modifiers.remove(Modifier.ABSTRACT); - for (TypeParameterElement typeParameterElement : target.getTypeParameters()) { - TypeVariable var = (TypeVariable) typeParameterElement.asType(); - bd.addTypeVariable(TypeVariableName.get(var)); - } - for(VariableElement p : target.getParameters()) - bd.addParameter(TypeName.get(p.asType()), p.getSimpleName().toString(), p.getModifiers().toArray(new Modifier[0])); - bd.varargs(target.isVarArgs()); - for(TypeMirror thrownType : target.getThrownTypes()) - bd.addException(TypeName.get(thrownType)); - return bd; - } - /** * Finds any method annotated with {@link FindMethod} or {@link FindField} within the given * class, and builds the {@link MethodSpec} necessary for building it. @@ -540,7 +511,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { ExecutableElement targetMethod = findRealMethodFromStub(m); - MethodSpec.Builder b = unsafeOverriding(m); + MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -569,7 +540,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { VariableElement targetField = findField(m); - MethodSpec.Builder b = unsafeOverriding(m); + MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper); -- cgit v1.2.3-56-ga3b1 From 1a4716dafedd7008b8b66e587cb8f35570be0363 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 20:32:12 +0100 Subject: chore: use String format --- src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java | 2 +- src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java | 2 +- src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java index dd75a08..76f12a5 100644 --- a/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java +++ b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java @@ -17,6 +17,6 @@ public class InvalidResourceException extends RuntimeException { * @param name the resource name */ public InvalidResourceException(String name) { - super("Specified resource " + name + " was not found!"); + super(String.format("Specified resource %s was not found!", name)); } } diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java index 40b0b65..e943c01 100644 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java @@ -12,7 +12,7 @@ public class MappingNotFoundException extends RuntimeException { * @param mapping the relevant mapping */ public MappingNotFoundException(String mapping) { - super("Could not find mapping for " + mapping + "!"); + super(String.format("Could not find mapping for %s!", mapping)); } /** diff --git a/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java index 5be3d77..1b63ea5 100644 --- a/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java @@ -10,6 +10,6 @@ public class TargetNotFoundException extends RuntimeException { * @param stub the stub's name (and descriptor possibly) */ public TargetNotFoundException(String stub) { - super("Could not find member corresponding to stub: " + stub); + super(String.format("Could not find member corresponding to stub: %s.")); } } -- cgit v1.2.3-56-ga3b1 From 062f8117b9f00459c52d91e3e30b5e65e77604d7 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Wed, 15 Mar 2023 20:32:44 +0100 Subject: fix: fixed failure to write descriptor of parametrised type --- src/main/java/ftbsc/lll/processor/tools/ASTUtils.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index faac3dc..fe72167 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -1,9 +1,6 @@ package ftbsc.lll.processor.tools; -import com.squareup.javapoet.ArrayTypeName; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.*; import ftbsc.lll.tools.DescriptorBuilder; import javax.annotation.processing.ProcessingEnvironment; @@ -53,8 +50,8 @@ public class ASTUtils { desc.append("["); type = ((ArrayTypeName) type).componentType; } - if(type instanceof ClassName) { - ClassName var = (ClassName) type; + if(type instanceof ClassName || type instanceof ParameterizedTypeName) { + ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type; desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0)); } else { if(TypeName.BOOLEAN.equals(type)) -- cgit v1.2.3-56-ga3b1 From f85c868e10fead010a58f5310b33a8396379b70b Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 16 Mar 2023 10:56:46 +0100 Subject: chore: javadocs for annotations --- .../java/ftbsc/lll/processor/annotations/FindField.java | 10 ++++++++++ .../java/ftbsc/lll/processor/annotations/FindMethod.java | 16 ++++++++++++++++ .../java/ftbsc/lll/processor/annotations/Injector.java | 11 +++++++++++ .../lll/processor/annotations/MultipleInjectors.java | 8 ++++++++ .../java/ftbsc/lll/processor/annotations/Target.java | 2 +- 5 files changed, 46 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/annotations/FindField.java b/src/main/java/ftbsc/lll/processor/annotations/FindField.java index c13fd46..40de173 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/FindField.java +++ b/src/main/java/ftbsc/lll/processor/annotations/FindField.java @@ -16,6 +16,16 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) public @interface FindField { + /** + * @return the {@link Class} object containing the desired field, + * or the {@link Object} class if not specified (the {@link Class} + * from {@link Patch#value()} is instead used) + */ Class parent() default Object.class; + + /** + * @return the name of the field, will default to the empty string + * (the name of the annotated method will instead be used) + */ String name() default ""; } diff --git a/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java b/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java index 76fe560..1705619 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java +++ b/src/main/java/ftbsc/lll/processor/annotations/FindMethod.java @@ -17,7 +17,23 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) public @interface FindMethod { + /** + * @return the {@link Class} object containing the desired method, + * or the {@link Object} class if not specified (the {@link Class} + * from {@link Patch#value()} is instead used) + */ Class parent() default Object.class; + + /** + * @return the name of the method, will default to the empty string + * (the name of the annotated method will instead be used) + */ String name() default ""; + + /** + * @return a list of the parameters of the method, will default to empty + * array (in that case, an attempt will be made to match a method without + * args first) + */ Class[] params() default {}; } diff --git a/src/main/java/ftbsc/lll/processor/annotations/Injector.java b/src/main/java/ftbsc/lll/processor/annotations/Injector.java index c26f704..4b74961 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/Injector.java +++ b/src/main/java/ftbsc/lll/processor/annotations/Injector.java @@ -11,6 +11,7 @@ import java.lang.annotation.RetentionPolicy; * as parameters. It will be discarded otherwise. * It will also be discarded unless the containing class is annotated with {@link Patch} * and another method within the class is annotated with {@link Target}. + * This annotation may be added multiple times, in order to target multiple methods. * @see Patch * @see Target */ @@ -18,6 +19,16 @@ import java.lang.annotation.RetentionPolicy; @Repeatable(MultipleInjectors.class) @java.lang.annotation.Target(ElementType.METHOD) public @interface Injector { + /** + * @return the name of the stub annotated with {@link Target} this is referring to. + * @since 0.3.0 + */ String targetName() default ""; + + /** + * @return the parameters of the stub annotated with {@link Target} this is referring + * to (used to discern in case of method stubs by the same name) + * @since 0.3.0 + */ Class[] params() default {}; } diff --git a/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java b/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java index 5c6382e..8b4b3f8 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java +++ b/src/main/java/ftbsc/lll/processor/annotations/MultipleInjectors.java @@ -1,11 +1,19 @@ package ftbsc.lll.processor.annotations; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * Used to support {@link Injector} as a {@link Repeatable} annotation. + * @since 0.3.0 + */ @Retention(RetentionPolicy.CLASS) @java.lang.annotation.Target(ElementType.METHOD) public @interface MultipleInjectors { + /** + * @return the {@link Injector} annotations, as an array + */ Injector[] value(); } diff --git a/src/main/java/ftbsc/lll/processor/annotations/Target.java b/src/main/java/ftbsc/lll/processor/annotations/Target.java index 99b8552..dbe7cf6 100644 --- a/src/main/java/ftbsc/lll/processor/annotations/Target.java +++ b/src/main/java/ftbsc/lll/processor/annotations/Target.java @@ -22,7 +22,7 @@ public @interface Target { * and to only check parameters if further clarification is needed. * @implNote While non-strict mode is more computationally efficient, it's ultimately not * relevant, as it only matters at compile time. Do not set this to false unless - * you are sure know what you're doing. + * you know what you're doing. * @since 0.3.0 */ boolean strict() default true; -- cgit v1.2.3-56-ga3b1 From dca13711d5126d83510a77b58c1a2675fce8028b Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 16 Mar 2023 15:26:46 +0100 Subject: feat: major code refactor, potential fix for several logical bugs in FindMethod and FindField --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 184 +++++++++------------ 1 file changed, 78 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 2040f20..04fcc4a 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -16,16 +16,17 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.*; +import java.lang.annotation.Annotation; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import static ftbsc.lll.processor.tools.ASTUtils.*; @@ -101,7 +102,7 @@ public class LilleroProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { - if(annotation.getQualifiedName().toString().equals(Patch.class.getName())) { + if(annotation.getQualifiedName().contentEquals(Patch.class.getName())) { Set validInjectors = roundEnv.getElementsAnnotatedWith(annotation) .stream() @@ -163,29 +164,19 @@ public class LilleroProcessor extends AbstractProcessor { /** * Finds the class name and maps it to the correct format. - * @param patchAnn the {@link Patch} annotation containing target class info - * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param mapper the {@link ObfuscationMapper} to use, may be null + * + * @param patchAnn the {@link Patch} annotation containing target class info + * @param finderAnn an annotation containing metadata to fall back on, may be null + * @param parentFun the function to get the parent from the finderAnn * @return the fully qualified class name * @since 0.3.0 */ - private static String findClassName(Patch patchAnn, FindMethod methodAnn, ObfuscationMapper mapper) { + private static String findClassName(Patch patchAnn, T finderAnn, Function> parentFun) { String fullyQualifiedName = - methodAnn == null || methodAnn.parent() == Object.class + finderAnn == null || parentFun.apply(finderAnn) == Object.class ? getClassFullyQualifiedName(patchAnn, Patch::value) - : getClassFullyQualifiedName(methodAnn, FindMethod::parent); - return findClassName(fullyQualifiedName, mapper); - } - - /** - * Finds the class name and maps it to the correct format. - * @param patchAnn the {@link Patch} annotation containing target class info - * @param mapper the {@link ObfuscationMapper} to use, may be null - * @return the internal class name - * @since 0.3.0 - */ - private static String findClassName(Patch patchAnn, ObfuscationMapper mapper) { - return findClassName(patchAnn, null, mapper); + : getClassFullyQualifiedName(finderAnn, parentFun); + return findClassName(fullyQualifiedName, null); } /** @@ -204,35 +195,9 @@ public class LilleroProcessor extends AbstractProcessor { } } - /** - * Finds the method name and maps it to the correct format. - * - * @param parentFQN the already mapped FQN of the parent class - * @param methodAnn the {@link FindMethod} annotation to fall back on, may be null - * @param stub the {@link ExecutableElement} for the stub - * @return the internal class name - * @since 0.3.0 - */ - private String findMethodName(String parentFQN, FindMethod methodAnn, ExecutableElement stub) { - String methodName = methodAnn == null ? stub.getSimpleName().toString() : methodAnn.name(); - String methodDescriptor; - if(methodAnn == null) - methodDescriptor = descriptorFromExecutableElement(stub); - else methodDescriptor = methodDescriptorFromParams(methodAnn, FindMethod::params, processingEnv.getElementUtils()); - try { - methodName = findMemberName(parentFQN, methodName, methodDescriptor, null); - } catch(MappingNotFoundException e) { - //not found: try again with the name of the annotated method - if(methodAnn == null) { - methodName = findMemberName(parentFQN, stub.getSimpleName().toString(), methodDescriptor, null); - } else throw e; - } - return methodName; - } - /** * Finds a method given name, container and descriptor. - * @param fullyQualifiedNameParent the fully qualified name of the parent class of the method + * @param parentFQN the fully qualified name of the parent class of the method * @param name the name to search for * @param descr the descriptor to search for * @param strict whether the search should be strict (see {@link Target#strict()} for more info) @@ -241,10 +206,10 @@ public class LilleroProcessor extends AbstractProcessor { * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private ExecutableElement findMethod(String fullyQualifiedNameParent, String name, String descr, boolean strict) { - TypeElement parent = processingEnv.getElementUtils().getTypeElement(fullyQualifiedNameParent); + private ExecutableElement findMethod(String parentFQN, String name, String descr, boolean strict) { + TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN); if(parent == null) - throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", fullyQualifiedNameParent)); + throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", parentFQN)); //try to find by name List candidates = parent.getEnclosedElements() @@ -259,7 +224,7 @@ public class LilleroProcessor extends AbstractProcessor { return candidates.get(0); if(descr == null) { throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, fullyQualifiedNameParent) + String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) ); } else { candidates = candidates.stream() @@ -271,62 +236,62 @@ public class LilleroProcessor extends AbstractProcessor { throw new TargetNotFoundException(String.format("%s %s", name, descr)); if(candidates.size() > 1) throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, fullyQualifiedNameParent) + String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) ); return candidates.get(0); } } /** - * Finds the real method corresponding to a stub. + * Finds the real class member (field or method) corresponding to a stub annotated with + * {@link Target} or {@link FindMethod} or {@link FindField}. * @param stub the {@link ExecutableElement} for the stub - * @return the desired method, if it exists + * @return the {@link Element} corresponding to the method or field * @throws AmbiguousDefinitionException if it finds more than one candidate * @throws TargetNotFoundException if it finds no valid candidate * @since 0.3.0 */ - private ExecutableElement findRealMethodFromStub(ExecutableElement stub) { - Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); - FindMethod findAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info - Target target = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled - String parentFQN = findClassName(patchAnn, findAnn, null); - String methodName = findMethodName(findClassName(patchAnn, findAnn, null), findAnn, stub); - return findMethod( - parentFQN, - methodName, - descriptorFromExecutableElement(stub), - target != null && target.strict()); - } - - /** - * Finds the real field corresponding to a stub. - * @param stub the {@link ExecutableElement} for the stub - * @return the desired method, if it exists - * @throws TargetNotFoundException if it finds no valid candidate - * @since 0.3.0 - */ - private VariableElement findField(ExecutableElement stub) { + private Element findMemberFromStub(ExecutableElement stub) { + //the parent always has a @Patch annotation Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); - FindField fieldAnn = stub.getAnnotation(FindField.class); - String parentName; - if(fieldAnn.parent().equals(Object.class)) - parentName = getClassFullyQualifiedName(patchAnn, Patch::value); - else parentName = getClassFullyQualifiedName(fieldAnn, FindField::parent); - parentName = findClassName(parentName, null); - String name = fieldAnn.name().equals("") - ? stub.getSimpleName().toString() - : fieldAnn.name(); - TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentName); - List candidates = - parent.getEnclosedElements() - .stream() - .filter(f -> f instanceof VariableElement) - .filter(f -> f.getSimpleName().contentEquals(name)) - .map(f -> (VariableElement) f) - .collect(Collectors.toList()); - if(candidates.size() == 0) - throw new TargetNotFoundException(stub.getSimpleName().toString()); - else return candidates.get(0); //there can only ever be one + //there should ever only be one of these + Target targetAnn = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled + FindMethod findMethodAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info + FindField findFieldAnn = stub.getAnnotation(FindField.class); + String parentFQN, memberName; + if(findFieldAnn == null) { //methods + parentFQN = findClassName(patchAnn, findMethodAnn, FindMethod::parent); + String methodDescriptor = + findMethodAnn != null + ? methodDescriptorFromParams(findMethodAnn, FindMethod::params, processingEnv.getElementUtils()) + : descriptorFromExecutableElement(stub); + memberName = + findMethodAnn != null && !findMethodAnn.name().equals("") + ? findMethodAnn.name() + : stub.getSimpleName().toString(); + return findMethod( + parentFQN, + memberName, + methodDescriptor, + targetAnn != null && targetAnn.strict() + ); + } else { //fields + parentFQN = findClassName(patchAnn, findFieldAnn, FindField::parent); + memberName = findFieldAnn.name().equals("") + ? stub.getSimpleName().toString() + : findFieldAnn.name(); + TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN); + List candidates = + parent.getEnclosedElements() + .stream() + .filter(f -> f instanceof VariableElement) + .filter(f -> f.getSimpleName().contentEquals(memberName)) + .map(f -> (VariableElement) f) + .collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(stub.getSimpleName().toString()); + else return candidates.get(0); //there can only ever be one + } } /** @@ -337,7 +302,9 @@ public class LilleroProcessor extends AbstractProcessor { private void generateInjectors(TypeElement cl) { //find class information Patch patchAnn = cl.getAnnotation(Patch.class); - String targetClassFQN = findClassName(patchAnn, mapper).replace('/', '.'); + String targetClassFQN = + findClassName(getClassFullyQualifiedName(patchAnn, Patch::value), this.mapper) + .replace('/', '.'); //find package information Element packageElement = cl.getEnclosingElement(); @@ -371,7 +338,7 @@ public class LilleroProcessor extends AbstractProcessor { injectionCandidates = injectionCandidates .stream() - .filter(i -> i.getSimpleName().toString().equals(injectorAnn.targetName())) + .filter(i -> i.getSimpleName().contentEquals(injectorAnn.targetName())) .collect(Collectors.toList()); } else if(targets.size() == 1) { //case 2: there is only one target @@ -432,7 +399,7 @@ public class LilleroProcessor extends AbstractProcessor { String targetMethodDescriptor = descriptorFromExecutableElement(toGenerate.get(injName).target); String targetMethodName = findMemberName(targetClassFQN, toGenerate.get(injName).target.getSimpleName().toString(), targetMethodDescriptor, this.mapper); - MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).target) + MethodSpec stubOverride = MethodSpec.overriding(toGenerate.get(injName).targetStub) .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") .build(); @@ -510,7 +477,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { - ExecutableElement targetMethod = findRealMethodFromStub(m); + ExecutableElement targetMethod = (ExecutableElement) findMemberFromStub(m); MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -539,7 +506,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { - VariableElement targetField = findField(m); + VariableElement targetField = (VariableElement) findMemberFromStub(m); MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -553,8 +520,8 @@ public class LilleroProcessor extends AbstractProcessor { b.addStatement("bd.setParent($S)", ((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString()); for(Modifier mod : targetField.getModifiers()) - b.addStatement("bd.addModifier($L)", mapModifier(mod)); + b.addStatement("bd.setType($T.class)", targetField.asType()); b.addStatement("return bd.build()"); @@ -584,26 +551,31 @@ public class LilleroProcessor extends AbstractProcessor { * Container for information about a class that is to be generated. * Only used internally. */ - private static class InjectorInfo { + public class InjectorInfo { /** * The {@link ExecutableElement} corresponding to the injector method. */ public final ExecutableElement injector; + /** + * The {@link ExecutableElement} corresponding to the target method stub. + */ + public final ExecutableElement targetStub; /** * The {@link ExecutableElement} corresponding to the target method. */ - public final ExecutableElement target; + private final ExecutableElement target; /** * Public constructor. * @param injector the injector {@link ExecutableElement} - * @param target the target {@link ExecutableElement} + * @param targetStub the target {@link ExecutableElement} */ - public InjectorInfo(ExecutableElement injector, ExecutableElement target) { + public InjectorInfo(ExecutableElement injector, ExecutableElement targetStub) { this.injector = injector; - this.target = target; + this.targetStub = targetStub; + this.target = (ExecutableElement) findMemberFromStub(targetStub); } } } \ No newline at end of file -- cgit v1.2.3-56-ga3b1 From 2999aef5a5c8bb5385644751372158eea5d4a858 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 16 Mar 2023 15:40:11 +0100 Subject: chore: moved methods around, should now be a lot more organised --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 176 +-------------- .../java/ftbsc/lll/processor/tools/ASTUtils.java | 237 ++++++++++++++------- .../ftbsc/lll/processor/tools/JavaPoetUtils.java | 110 ++++++++++ 3 files changed, 276 insertions(+), 247 deletions(-) create mode 100644 src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 04fcc4a..d94d44d 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -4,8 +4,6 @@ import com.squareup.javapoet.*; import ftbsc.lll.IInjector; import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.InvalidResourceException; -import ftbsc.lll.exceptions.MappingNotFoundException; -import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.processor.annotations.*; import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import ftbsc.lll.proxies.FieldProxy; @@ -21,16 +19,15 @@ import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.*; -import java.lang.annotation.Annotation; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; import static ftbsc.lll.processor.tools.ASTUtils.*; import static ftbsc.lll.processor.tools.ASTUtils.getClassFullyQualifiedName; +import static ftbsc.lll.processor.tools.JavaPoetUtils.*; /** * The actual annotation processor behind the magic. @@ -59,6 +56,7 @@ public class LilleroProcessor extends AbstractProcessor { * @param processingEnv environment to access facilities the tool framework * provides to the processor * @throws IllegalStateException if this method is called more than once. + * @since 0.3.0 */ @Override public synchronized void init(ProcessingEnvironment processingEnv) { @@ -147,153 +145,6 @@ public class LilleroProcessor extends AbstractProcessor { } } - /** - * Finds the class name and maps it to the correct format. - * @param name the fully qualified name of the class to convert - * @param mapper the {@link ObfuscationMapper} to use, may be null - * @return the fully qualified class name - * @since 0.3.0 - */ - private static String findClassName(String name, ObfuscationMapper mapper) { - try { - return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); - } catch(MappingNotFoundException e) { - return name; - } - } - - /** - * Finds the class name and maps it to the correct format. - * - * @param patchAnn the {@link Patch} annotation containing target class info - * @param finderAnn an annotation containing metadata to fall back on, may be null - * @param parentFun the function to get the parent from the finderAnn - * @return the fully qualified class name - * @since 0.3.0 - */ - private static String findClassName(Patch patchAnn, T finderAnn, Function> parentFun) { - String fullyQualifiedName = - finderAnn == null || parentFun.apply(finderAnn) == Object.class - ? getClassFullyQualifiedName(patchAnn, Patch::value) - : getClassFullyQualifiedName(finderAnn, parentFun); - return findClassName(fullyQualifiedName, null); - } - - /** - * Finds the member name and maps it to the correct format. - * @param parentFQN the already mapped FQN of the parent class - * @param memberName the name of the member - * @param mapper the {@link ObfuscationMapper} to use, may be null - * @return the internal class name - * @since 0.3.0 - */ - private static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { - try { - return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); - } catch(MappingNotFoundException e) { - return memberName; - } - } - - /** - * Finds a method given name, container and descriptor. - * @param parentFQN the fully qualified name of the parent class of the method - * @param name the name to search for - * @param descr the descriptor to search for - * @param strict whether the search should be strict (see {@link Target#strict()} for more info) - * @return the desired method, if it exists - * @throws AmbiguousDefinitionException if it finds more than one candidate - * @throws TargetNotFoundException if it finds no valid candidate - * @since 0.3.0 - */ - private ExecutableElement findMethod(String parentFQN, String name, String descr, boolean strict) { - TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN); - if(parent == null) - throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", parentFQN)); - - //try to find by name - List candidates = parent.getEnclosedElements() - .stream() - .filter(e -> e instanceof ExecutableElement) - .map(e -> (ExecutableElement) e) - .filter(e -> e.getSimpleName().contentEquals(name)) - .collect(Collectors.toList()); - if(candidates.size() == 0) - throw new TargetNotFoundException(String.format("%s %s", name, descr)); - if(candidates.size() == 1 && !strict) - return candidates.get(0); - if(descr == null) { - throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) - ); - } else { - candidates = candidates.stream() - .filter(strict - ? c -> descr.equals(descriptorFromExecutableElement(c)) - : c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0]) - ).collect(Collectors.toList()); - if(candidates.size() == 0) - throw new TargetNotFoundException(String.format("%s %s", name, descr)); - if(candidates.size() > 1) - throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) - ); - return candidates.get(0); - } - } - - /** - * Finds the real class member (field or method) corresponding to a stub annotated with - * {@link Target} or {@link FindMethod} or {@link FindField}. - * @param stub the {@link ExecutableElement} for the stub - * @return the {@link Element} corresponding to the method or field - * @throws AmbiguousDefinitionException if it finds more than one candidate - * @throws TargetNotFoundException if it finds no valid candidate - * @since 0.3.0 - */ - private Element findMemberFromStub(ExecutableElement stub) { - //the parent always has a @Patch annotation - Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); - //there should ever only be one of these - Target targetAnn = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled - FindMethod findMethodAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info - FindField findFieldAnn = stub.getAnnotation(FindField.class); - String parentFQN, memberName; - if(findFieldAnn == null) { //methods - parentFQN = findClassName(patchAnn, findMethodAnn, FindMethod::parent); - String methodDescriptor = - findMethodAnn != null - ? methodDescriptorFromParams(findMethodAnn, FindMethod::params, processingEnv.getElementUtils()) - : descriptorFromExecutableElement(stub); - memberName = - findMethodAnn != null && !findMethodAnn.name().equals("") - ? findMethodAnn.name() - : stub.getSimpleName().toString(); - return findMethod( - parentFQN, - memberName, - methodDescriptor, - targetAnn != null && targetAnn.strict() - ); - } else { //fields - parentFQN = findClassName(patchAnn, findFieldAnn, FindField::parent); - memberName = findFieldAnn.name().equals("") - ? stub.getSimpleName().toString() - : findFieldAnn.name(); - TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN); - List candidates = - parent.getEnclosedElements() - .stream() - .filter(f -> f instanceof VariableElement) - .filter(f -> f.getSimpleName().contentEquals(memberName)) - .map(f -> (VariableElement) f) - .collect(Collectors.toList()); - if(candidates.size() == 0) - throw new TargetNotFoundException(stub.getSimpleName().toString()); - else return candidates.get(0); //there can only ever be one - } - } - /** * Generates the Injector(s) contained in the given class. * Basically implements the {@link IInjector} interface for you. @@ -448,21 +299,6 @@ public class LilleroProcessor extends AbstractProcessor { } } - /** - * Builds a {@link MethodSpec} for a public method whose body simply returns a {@link String}. - * @param name the name of the method - * @param returnString the {@link String} to return - * @return the built {@link MethodSpec} - */ - private static MethodSpec buildStringReturnMethod(String name, String returnString) { - return MethodSpec.methodBuilder(name) - .addModifiers(Modifier.PUBLIC) - .addAnnotation(Override.class) - .returns(String.class) - .addStatement("return $S", returnString) - .build(); - } - /** * Finds any method annotated with {@link FindMethod} or {@link FindField} within the given * class, and builds the {@link MethodSpec} necessary for building it. @@ -477,7 +313,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny .forEach(m -> { - ExecutableElement targetMethod = (ExecutableElement) findMemberFromStub(m); + ExecutableElement targetMethod = (ExecutableElement) findMemberFromStub(m, processingEnv); MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -506,7 +342,7 @@ public class LilleroProcessor extends AbstractProcessor { .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) .filter(m -> !m.getModifiers().contains(Modifier.FINAL)) .forEach(m -> { - VariableElement targetField = (VariableElement) findMemberFromStub(m); + VariableElement targetField = (VariableElement) findMemberFromStub(m, processingEnv); MethodSpec.Builder b = MethodSpec.overriding(m); String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper); @@ -551,7 +387,7 @@ public class LilleroProcessor extends AbstractProcessor { * Container for information about a class that is to be generated. * Only used internally. */ - public class InjectorInfo { + private class InjectorInfo { /** * The {@link ExecutableElement} corresponding to the injector method. */ @@ -575,7 +411,7 @@ public class LilleroProcessor extends AbstractProcessor { public InjectorInfo(ExecutableElement injector, ExecutableElement targetStub) { this.injector = injector; this.targetStub = targetStub; - this.target = (ExecutableElement) findMemberFromStub(targetStub); + this.target = (ExecutableElement) findMemberFromStub(targetStub, processingEnv); } } } \ No newline at end of file diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index fe72167..0248051 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -1,12 +1,17 @@ package ftbsc.lll.processor.tools; import com.squareup.javapoet.*; -import ftbsc.lll.tools.DescriptorBuilder; +import ftbsc.lll.exceptions.AmbiguousDefinitionException; +import ftbsc.lll.exceptions.MappingNotFoundException; +import ftbsc.lll.exceptions.TargetNotFoundException; +import ftbsc.lll.processor.annotations.FindField; +import ftbsc.lll.processor.annotations.FindMethod; +import ftbsc.lll.processor.annotations.Patch; +import ftbsc.lll.processor.annotations.Target; +import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.*; import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.MirroredTypesException; import javax.lang.model.type.TypeMirror; @@ -18,8 +23,11 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import static ftbsc.lll.processor.tools.JavaPoetUtils.descriptorFromExecutableElement; +import static ftbsc.lll.processor.tools.JavaPoetUtils.methodDescriptorFromParams; + /** - * Collection of static utils that didn't really fit into the main class. + * Collection of AST-related static utils that didn't really fit into the main class. */ public class ASTUtils { /** @@ -38,67 +46,6 @@ public class ASTUtils { .collect(Collectors.toList()); } - /** - * 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 || type instanceof ParameterizedTypeName) { - ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type; - desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0)); - } else { - if(TypeName.BOOLEAN.equals(type)) - desc.append("Z"); - else if(TypeName.CHAR.equals(type)) - desc.append("C"); - else if(TypeName.BYTE.equals(type)) - desc.append("B"); - else if(TypeName.SHORT.equals(type)) - desc.append("S"); - else if(TypeName.INT.equals(type)) - desc.append("I"); - else if(TypeName.FLOAT.equals(type)) - desc.append("F"); - else if(TypeName.LONG.equals(type)) - desc.append("J"); - else if(TypeName.DOUBLE.equals(type)) - desc.append("D"); - else if(TypeName.VOID.equals(type)) - desc.append("V"); - } - return desc.toString(); - } - - /** - * Builds a type descriptor from the given {@link TypeMirror}. - * @param t the {@link TypeMirror} representing the desired type - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromType(TypeMirror t) { - return descriptorFromType(TypeName.get(t)); - } - - /** - * Builds a method descriptor from the given {@link ExecutableElement}. - * @param m the {@link ExecutableElement} for the method - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromExecutableElement(ExecutableElement m) { - StringBuilder methodSignature = new StringBuilder(); - methodSignature.append("("); - m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType()))); - methodSignature.append(")"); - methodSignature.append(descriptorFromType(m.getReturnType())); - return methodSignature.toString(); - } - /** * Maps a {@link javax.lang.model.element.Modifier} to its reflective * {@link java.lang.reflect.Modifier} equivalent. @@ -172,18 +119,154 @@ public class ASTUtils { return params; } + /** - * Builds a (partial, not including the return type) method descriptor from its parameters - * @param ann the annotation containing the class - * @param fun the annotation function returning the class - * @return the method descriptor + * Finds the class name and maps it to the correct format. + * @param name the fully qualified name of the class to convert + * @param mapper the {@link ObfuscationMapper} to use, may be null + * @return the fully qualified class name + * @since 0.3.0 */ - public static String methodDescriptorFromParams(T ann, Function[]> fun, Elements elementUtils) { - List mirrors = classArrayFromAnnotation(ann, fun, elementUtils); - StringBuilder sb = new StringBuilder("("); - for(TypeMirror t : mirrors) - sb.append(descriptorFromType(t)); - sb.append(")"); - return sb.toString(); + public static String findClassName(String name, ObfuscationMapper mapper) { + try { + return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.'); + } catch(MappingNotFoundException e) { + return name; + } + } + + /** + * Finds the class name and maps it to the correct format. + * + * @param patchAnn the {@link Patch} annotation containing target class info + * @param finderAnn an annotation containing metadata to fall back on, may be null + * @param parentFun the function to get the parent from the finderAnn + * @return the fully qualified class name + * @since 0.3.0 + */ + private static String findClassName(Patch patchAnn, T finderAnn, Function> parentFun) { + String fullyQualifiedName = + finderAnn == null || parentFun.apply(finderAnn) == Object.class + ? getClassFullyQualifiedName(patchAnn, Patch::value) + : getClassFullyQualifiedName(finderAnn, parentFun); + return findClassName(fullyQualifiedName, null); + } + + /** + * Finds the member name and maps it to the correct format. + * @param parentFQN the already mapped FQN of the parent class + * @param memberName the name of the member + * @param mapper the {@link ObfuscationMapper} to use, may be null + * @return the internal class name + * @since 0.3.0 + */ + public static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { + try { + return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); + } catch(MappingNotFoundException e) { + return memberName; + } + } + + /** + * Finds a method given name, container and descriptor. + * @param parentFQN the fully qualified name of the parent class of the method + * @param name the name to search for + * @param descr the descriptor to search for + * @param strict whether the search should be strict (see {@link Target#strict()} for more info) + * @param env the {@link ProcessingEnvironment} to perform the operation in + * @return the desired method, if it exists + * @throws AmbiguousDefinitionException if it finds more than one candidate + * @throws TargetNotFoundException if it finds no valid candidate + * @since 0.3.0 + */ + private static ExecutableElement findMethod(String parentFQN, String name, String descr, boolean strict, ProcessingEnvironment env) { + TypeElement parent = env.getElementUtils().getTypeElement(parentFQN); + if(parent == null) + throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", parentFQN)); + + //try to find by name + List candidates = parent.getEnclosedElements() + .stream() + .filter(e -> e instanceof ExecutableElement) + .map(e -> (ExecutableElement) e) + .filter(e -> e.getSimpleName().contentEquals(name)) + .collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(String.format("%s %s", name, descr)); + if(candidates.size() == 1 && !strict) + return candidates.get(0); + if(descr == null) { + throw new AmbiguousDefinitionException( + String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) + ); + } else { + candidates = candidates.stream() + .filter(strict + ? c -> descr.equals(descriptorFromExecutableElement(c)) + : c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0]) + ).collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(String.format("%s %s", name, descr)); + if(candidates.size() > 1) + throw new AmbiguousDefinitionException( + String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN) + ); + return candidates.get(0); + } + } + + /** + * Finds the real class member (field or method) corresponding to a stub annotated with + * {@link Target} or {@link FindMethod} or {@link FindField}. + * @param stub the {@link ExecutableElement} for the stub + * @param env the {@link ProcessingEnvironment} to perform the operation in + * @return the {@link Element} corresponding to the method or field + * @throws AmbiguousDefinitionException if it finds more than one candidate + * @throws TargetNotFoundException if it finds no valid candidate + * @since 0.3.0 + */ + public static Element findMemberFromStub(ExecutableElement stub, ProcessingEnvironment env) { + //the parent always has a @Patch annotation + Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); + //there should ever only be one of these + Target targetAnn = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled + FindMethod findMethodAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info + FindField findFieldAnn = stub.getAnnotation(FindField.class); + String parentFQN, memberName; + if(findFieldAnn == null) { //methods + parentFQN = findClassName(patchAnn, findMethodAnn, FindMethod::parent); + String methodDescriptor = + findMethodAnn != null + ? methodDescriptorFromParams(findMethodAnn, FindMethod::params, env.getElementUtils()) + : descriptorFromExecutableElement(stub); + memberName = + findMethodAnn != null && !findMethodAnn.name().equals("") + ? findMethodAnn.name() + : stub.getSimpleName().toString(); + return findMethod( + parentFQN, + memberName, + methodDescriptor, + targetAnn != null && targetAnn.strict(), + env + ); + } else { //fields + parentFQN = findClassName(patchAnn, findFieldAnn, FindField::parent); + memberName = findFieldAnn.name().equals("") + ? stub.getSimpleName().toString() + : findFieldAnn.name(); + TypeElement parent = env.getElementUtils().getTypeElement(parentFQN); + List candidates = + parent.getEnclosedElements() + .stream() + .filter(f -> f instanceof VariableElement) + .filter(f -> f.getSimpleName().contentEquals(memberName)) + .map(f -> (VariableElement) f) + .collect(Collectors.toList()); + if(candidates.size() == 0) + throw new TargetNotFoundException(stub.getSimpleName().toString()); + else return candidates.get(0); //there can only ever be one + } } } diff --git a/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java b/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java new file mode 100644 index 0000000..18f9ed6 --- /dev/null +++ b/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java @@ -0,0 +1,110 @@ +package ftbsc.lll.processor.tools; + +import com.squareup.javapoet.*; +import ftbsc.lll.tools.DescriptorBuilder; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.function.Function; + +import static ftbsc.lll.processor.tools.ASTUtils.classArrayFromAnnotation; + +/** + * Collection of static utils that rely on JavaPoet to function. + */ +public class JavaPoetUtils { + /** + * Builds a {@link MethodSpec} for a public method whose body simply returns a {@link String}. + * @param name the name of the method + * @param returnString the {@link String} to return + * @return the built {@link MethodSpec} + */ + public static MethodSpec buildStringReturnMethod(String name, String returnString) { + return MethodSpec.methodBuilder(name) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(String.class) + .addStatement("return $S", returnString) + .build(); + } + + /** + * 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 || type instanceof ParameterizedTypeName) { + ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type; + desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0)); + } else { + if(TypeName.BOOLEAN.equals(type)) + desc.append("Z"); + else if(TypeName.CHAR.equals(type)) + desc.append("C"); + else if(TypeName.BYTE.equals(type)) + desc.append("B"); + else if(TypeName.SHORT.equals(type)) + desc.append("S"); + else if(TypeName.INT.equals(type)) + desc.append("I"); + else if(TypeName.FLOAT.equals(type)) + desc.append("F"); + else if(TypeName.LONG.equals(type)) + desc.append("J"); + else if(TypeName.DOUBLE.equals(type)) + desc.append("D"); + else if(TypeName.VOID.equals(type)) + desc.append("V"); + } + return desc.toString(); + } + + /** + * Builds a type descriptor from the given {@link TypeMirror}. + * @param t the {@link TypeMirror} representing the desired type + * @return a {@link String} containing the relevant descriptor + */ + public static String descriptorFromType(TypeMirror t) { + return descriptorFromType(TypeName.get(t)); + } + + /** + * Builds a method descriptor from the given {@link ExecutableElement}. + * @param m the {@link ExecutableElement} for the method + * @return a {@link String} containing the relevant descriptor + */ + public static String descriptorFromExecutableElement(ExecutableElement m) { + StringBuilder methodSignature = new StringBuilder(); + methodSignature.append("("); + m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType()))); + methodSignature.append(")"); + methodSignature.append(descriptorFromType(m.getReturnType())); + return methodSignature.toString(); + } + + /** + * Builds a (partial, not including the return type) method descriptor from its parameters + * @param ann the annotation containing the class + * @param fun the annotation function returning the class + * @return the method descriptor + */ + public static String methodDescriptorFromParams(T ann, Function[]> fun, Elements elementUtils) { + List mirrors = classArrayFromAnnotation(ann, fun, elementUtils); + StringBuilder sb = new StringBuilder("("); + for(TypeMirror t : mirrors) + sb.append(descriptorFromType(t)); + sb.append(")"); + return sb.toString(); + } +} -- cgit v1.2.3-56-ga3b1 From 6a067132105964d6367885ba967f6ea33e4fd65a Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 16 Mar 2023 15:50:39 +0100 Subject: fix: fixed uncaught MirroredTypeException --- src/main/java/ftbsc/lll/processor/tools/ASTUtils.java | 14 ++++++++------ .../lll/processor/tools/obfuscation/ObfuscationMapper.java | 1 - 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index 0248051..ef50e52 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -137,18 +137,20 @@ public class ASTUtils { /** * Finds the class name and maps it to the correct format. - * * @param patchAnn the {@link Patch} annotation containing target class info - * @param finderAnn an annotation containing metadata to fall back on, may be null + * @param finderAnn an annotation containing metadata about the target, may be null * @param parentFun the function to get the parent from the finderAnn * @return the fully qualified class name * @since 0.3.0 */ private static String findClassName(Patch patchAnn, T finderAnn, Function> parentFun) { - String fullyQualifiedName = - finderAnn == null || parentFun.apply(finderAnn) == Object.class - ? getClassFullyQualifiedName(patchAnn, Patch::value) - : getClassFullyQualifiedName(finderAnn, parentFun); + String fullyQualifiedName; + if(finderAnn != null) { + fullyQualifiedName = getClassFullyQualifiedName(finderAnn, parentFun); + if(!fullyQualifiedName.equals("java.lang.Object")) + return findClassName(fullyQualifiedName, null); + } + fullyQualifiedName = getClassFullyQualifiedName(patchAnn, Patch::value); return findClassName(fullyQualifiedName, null); } diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java index 6da8c9b..ad4ced3 100644 --- a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java +++ b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java @@ -144,7 +144,6 @@ public class ObfuscationMapper { */ private final Map members; - /** * The constructor. It takes in the names (obfuscated and non-obfuscated) * of a class. -- cgit v1.2.3-56-ga3b1 From 6d1d648af437deaab29976c32a77c6cb7a2e5e30 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 16 Mar 2023 15:55:34 +0100 Subject: fix: epic printf fail --- src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java index 1b63ea5..c82e0fc 100644 --- a/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java +++ b/src/main/java/ftbsc/lll/exceptions/TargetNotFoundException.java @@ -10,6 +10,6 @@ public class TargetNotFoundException extends RuntimeException { * @param stub the stub's name (and descriptor possibly) */ public TargetNotFoundException(String stub) { - super(String.format("Could not find member corresponding to stub: %s.")); + super(String.format("Could not find member corresponding to stub: %s.", stub)); } } -- cgit v1.2.3-56-ga3b1 From 3b560f5990c9c96a7f31860735b0b60eb798e7d3 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Fri, 17 Mar 2023 18:42:27 +0100 Subject: fix: getParameters() not getTypeParameters() --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index d94d44d..a965068 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -329,8 +329,8 @@ public class LilleroProcessor extends AbstractProcessor { for(Modifier mod : targetMethod.getModifiers()) b.addStatement("bd.addModifier($L)", mapModifier(mod)); - for(TypeParameterElement p : targetMethod.getTypeParameters()) - b.addStatement("bd.addParameter($T.class)", p.asType()); + for(VariableElement p : targetMethod.getParameters()) + b.addStatement("bd.addParameter($T.class)",p.asType()); b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); b.addStatement("return bd.build()"); -- cgit v1.2.3-56-ga3b1 From 5fd954a1188afb230f2b1ae70834110e742a3c99 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Fri, 17 Mar 2023 19:09:32 +0100 Subject: fix: fixed NoClassDefFoundError for non-java classes --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index a965068..957785b 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -329,10 +329,16 @@ public class LilleroProcessor extends AbstractProcessor { for(Modifier mod : targetMethod.getModifiers()) b.addStatement("bd.addModifier($L)", mapModifier(mod)); - for(VariableElement p : targetMethod.getParameters()) - b.addStatement("bd.addParameter($T.class)",p.asType()); + for(VariableElement p : targetMethod.getParameters()) { + if(p.asType().getKind().isPrimitive()) + b.addStatement("bd.addParameter($T.class)", p.asType()); + else b.addStatement("bd.addParameter($S)", p.asType().toString()); + } + + if(targetMethod.getReturnType().getKind().isPrimitive()) + b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); + else b.addStatement("bd.setReturnType($S)", targetMethod.getReturnType().toString()); - b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); b.addStatement("return bd.build()"); generated.add(b.build()); @@ -358,7 +364,10 @@ public class LilleroProcessor extends AbstractProcessor { for(Modifier mod : targetField.getModifiers()) b.addStatement("bd.addModifier($L)", mapModifier(mod)); - b.addStatement("bd.setType($T.class)", targetField.asType()); + if(targetField.asType().getKind().isPrimitive()) + b.addStatement("bd.setType($T.class)", targetField.asType()); + else b.addStatement("bd.setType($S)", targetField.asType().toString()); + b.addStatement("return bd.build()"); generated.add(b.build()); -- cgit v1.2.3-56-ga3b1 From 6e2355997398923c73756c3ae319c6290977e614 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Fri, 17 Mar 2023 19:24:20 +0100 Subject: fix: support array levels --- .../java/ftbsc/lll/processor/LilleroProcessor.java | 6 ++--- .../java/ftbsc/lll/processor/tools/ASTUtils.java | 31 +++++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index 957785b..e023677 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -332,12 +332,12 @@ public class LilleroProcessor extends AbstractProcessor { for(VariableElement p : targetMethod.getParameters()) { if(p.asType().getKind().isPrimitive()) b.addStatement("bd.addParameter($T.class)", p.asType()); - else b.addStatement("bd.addParameter($S)", p.asType().toString()); + else b.addStatement("bd.addParameter($S, %L)", getInnermostComponentType(p.asType()), getArrayLevel(p.asType())); } if(targetMethod.getReturnType().getKind().isPrimitive()) b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); - else b.addStatement("bd.setReturnType($S)", targetMethod.getReturnType().toString()); + else b.addStatement("bd.setReturnType($S, %L)", getInnermostComponentType(targetMethod.getReturnType()), getArrayLevel(targetMethod.getReturnType())); b.addStatement("return bd.build()"); @@ -366,7 +366,7 @@ public class LilleroProcessor extends AbstractProcessor { if(targetField.asType().getKind().isPrimitive()) b.addStatement("bd.setType($T.class)", targetField.asType()); - else b.addStatement("bd.setType($S)", targetField.asType().toString()); + else b.addStatement("bd.setType($S, %L)", getInnermostComponentType(targetField.asType()), getArrayLevel(targetField.asType())); b.addStatement("return bd.build()"); diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java index ef50e52..1735b66 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java @@ -12,9 +12,7 @@ import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.*; -import javax.lang.model.type.MirroredTypeException; -import javax.lang.model.type.MirroredTypesException; -import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.*; import javax.lang.model.util.Elements; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -83,6 +81,33 @@ public class ASTUtils { } } + /** + * Calculates the array nesting level for a {@link TypeMirror}. + * @param t the type mirror to get it for + * @return the array nesting level + * @since 0.3.0 + */ + public static int getArrayLevel(TypeMirror t) { + int arrayLevel = 0; + while(t.getKind() == TypeKind.ARRAY) { + t = ((ArrayType) t).getComponentType(); + arrayLevel++; + } + return arrayLevel; + } + + /** + * Calculates the array nesting level for a {@link TypeMirror}. + * @param t the type mirror to get it for + * @return the array nesting level + * @since 0.3.0 + */ + public static TypeMirror getInnermostComponentType(TypeMirror t) { + while(t.getKind() == TypeKind.ARRAY) + t = ((ArrayType) t).getComponentType(); + return t; + } + /** * Safely extracts a {@link Class} from an annotation and gets its fully qualified name. * @param ann the annotation containing the class -- cgit v1.2.3-56-ga3b1 From bef6b17e1543794561ea1eb82504b38b82fad6f9 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Fri, 17 Mar 2023 19:26:51 +0100 Subject: fix: epic keyboard fail --- src/main/java/ftbsc/lll/processor/LilleroProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index e023677..432738c 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -332,12 +332,12 @@ public class LilleroProcessor extends AbstractProcessor { for(VariableElement p : targetMethod.getParameters()) { if(p.asType().getKind().isPrimitive()) b.addStatement("bd.addParameter($T.class)", p.asType()); - else b.addStatement("bd.addParameter($S, %L)", getInnermostComponentType(p.asType()), getArrayLevel(p.asType())); + else b.addStatement("bd.addParameter($S, $L)", getInnermostComponentType(p.asType()), getArrayLevel(p.asType())); } if(targetMethod.getReturnType().getKind().isPrimitive()) b.addStatement("bd.setReturnType($T.class)", targetMethod.getReturnType()); - else b.addStatement("bd.setReturnType($S, %L)", getInnermostComponentType(targetMethod.getReturnType()), getArrayLevel(targetMethod.getReturnType())); + else b.addStatement("bd.setReturnType($S, $L)", getInnermostComponentType(targetMethod.getReturnType()), getArrayLevel(targetMethod.getReturnType())); b.addStatement("return bd.build()"); @@ -366,7 +366,7 @@ public class LilleroProcessor extends AbstractProcessor { if(targetField.asType().getKind().isPrimitive()) b.addStatement("bd.setType($T.class)", targetField.asType()); - else b.addStatement("bd.setType($S, %L)", getInnermostComponentType(targetField.asType()), getArrayLevel(targetField.asType())); + else b.addStatement("bd.setType($S, $L)", getInnermostComponentType(targetField.asType()), getArrayLevel(targetField.asType())); b.addStatement("return bd.build()"); -- cgit v1.2.3-56-ga3b1