diff options
Diffstat (limited to 'src/main/java/ftbsc/lll/processor/tools')
8 files changed, 0 insertions, 1289 deletions
diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java deleted file mode 100644 index 0037886..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ /dev/null @@ -1,381 +0,0 @@ -package ftbsc.lll.processor.tools; - -import ftbsc.lll.exceptions.AmbiguousDefinitionException; -import ftbsc.lll.exceptions.MappingNotFoundException; -import ftbsc.lll.exceptions.NotAProxyException; -import ftbsc.lll.exceptions.TargetNotFoundException; -import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.containers.ClassContainer; -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; -import ftbsc.lll.proxies.ProxyType; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.*; -import javax.lang.model.type.*; -import java.lang.annotation.Annotation; -import java.util.Collection; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * Collection of AST-related 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 - * an {@link Element} from it. - * @param parent the parent {@link Element} to the desired element - * @param ann the {@link Class} corresponding to the desired annotation - * @param <T> the type of {@link Element} to use - * @return a {@link List} of {@link Element}s annotated with the given annotation - * @since 0.2.0 - */ - @SuppressWarnings("unchecked") - public static <T extends Element> List<T> findAnnotatedElement(Element parent, Class<? extends Annotation> ann) { - return parent.getEnclosedElements() - .stream() - .filter(e -> e.getAnnotationsByType(ann).length != 0) - .map(e -> (T) e) - .collect(Collectors.toList()); - } - - /** - * 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; - } - } - - /** - * Takes in a {@link Collection} of AST {@link Modifier}s and - * returns them mapped to their reflective integer equivalent. - * @param modifiers the {@link Modifier}s - * @return an integer value representing them - * @since 0.5.0 - */ - public static int mapModifiers(Collection<Modifier> modifiers) { - int i = 0; - for(Modifier m : modifiers) - i |= mapModifier(m); - return i; - } - - /** - * Safely extracts a {@link Class} from an annotation and gets a {@link TypeMirror} representing it. - * @param ann the annotation containing the class - * @param classFunction the annotation function returning the class - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @param <T> the type of the annotation carrying the information - * @return a {@link TypeMirror} representing the requested {@link Class} - * @since 0.3.0 - */ - public static <T extends Annotation> TypeMirror getTypeFromAnnotation( - T ann, Function<T, Class<?>> classFunction, ProcessingEnvironment env) { - try { - String fqn = classFunction.apply(ann).getCanonicalName(); - if(fqn == null) - fqn = ""; - return env.getElementUtils().getTypeElement(fqn).asType(); - } catch(MirroredTypeException e) { - return e.getTypeMirror(); - } - } - - /** - * Gets the internal name from an {@link TypeMirror}. - * @param type the {@link TypeMirror} in question - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return the internal name at compile time, or null if it wasn't a qualifiable - * @since 0.5.1 - */ - public static String internalNameFromType(TypeMirror type, ProcessingEnvironment env) { - //needed to actually turn elem into a TypeVariable, find it ignoring generics - Element elem = env.getTypeUtils().asElement(env.getTypeUtils().erasure(type)); - StringBuilder fqnBuilder = new StringBuilder(); - while(elem.getEnclosingElement() != null && elem.getEnclosingElement().getKind() != ElementKind.PACKAGE) { - fqnBuilder - .insert(0, elem.getSimpleName().toString()) - .insert(0, "$"); - elem = elem.getEnclosingElement(); - } - return fqnBuilder - .insert(0, env.getTypeUtils().erasure(elem.asType()).toString()) - .toString() - .replace('.', '/'); - } - - /** - * Builds a type descriptor from the given {@link TypeMirror}. - * @param t the {@link TypeMirror} representing the desired type - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromType(TypeMirror t, ProcessingEnvironment env) { - t = env.getTypeUtils().erasure(t); //type erasure - - StringBuilder desc = new StringBuilder(); - //add array brackets - while(t.getKind() == TypeKind.ARRAY) { - desc.append("["); - t = ((ArrayType) t).getComponentType(); - } - - if(t.getKind() == TypeKind.TYPEVAR) - t = ((TypeVariable) t).getUpperBound(); - - if(t.getKind() == TypeKind.DECLARED) - desc - .append("L") - .append(internalNameFromType(t, env)) - .append(";"); - else { - switch(t.getKind()) { - case BOOLEAN: - desc.append("Z"); - break; - case CHAR: - desc.append("C"); - break; - case BYTE: - desc.append("B"); - break; - case SHORT: - desc.append("S"); - break; - case INT: - desc.append("I"); - break; - case FLOAT: - desc.append("F"); - break; - case LONG: - desc.append("J"); - break; - case DOUBLE: - desc.append("D"); - break; - case VOID: - desc.append("V"); - break; - } - } - - return desc.toString(); - } - - /** - * Builds a method descriptor from the given {@link ExecutableElement}. - * @param m the {@link ExecutableElement} for the method - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return a {@link String} containing the relevant descriptor - */ - public static String descriptorFromExecutableElement(ExecutableElement m, ProcessingEnvironment env) { - StringBuilder methodSignature = new StringBuilder(); - methodSignature.append("("); - m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType(), env))); - methodSignature.append(")"); - methodSignature.append(descriptorFromType(m.getReturnType(), env)); - return methodSignature.toString(); - } - - /** - * Finds the class name and maps it to the correct format. - * @param name the fully qualified name of the class to convert - * @param options the {@link ProcessorOptions} to be used - * @return the fully qualified class name - * @since 0.3.0 - */ - public static String findClassName(String name, ProcessorOptions options) { - try { - return options.mapper == null ? name : options.mapper.obfuscateClass(name).replace('/', '.'); - } catch(MappingNotFoundException e) { - return name; - } - } - - /** - * Finds the member name and maps it to the correct format. - * @param parentFQN the unobfuscated FQN of the parent class - * @param memberName the name of the member - * @param methodDescriptor the descriptor of the method, may be null - * @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 member given the name, the container class and (if it's a method) the descriptor. - * @param parent the {@link ClassContainer} representing the parent - * @param name the name to search for - * @param descr the descriptor to search for, or null if it's not a method - * @param strict whether to perform lookup in strict mode (see {@link Target#strict()} for more information) - * @param field whether the member being searched is a field - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return the desired member, if it exists - * @throws AmbiguousDefinitionException if it finds more than one candidate - * @throws TargetNotFoundException if it finds no valid candidate - * @since 0.3.0 - */ - public static Element findMember( - ClassContainer parent, String name, String descr, - boolean strict, boolean field, ProcessingEnvironment env) { - if(parent.elem == null) - throw new TargetNotFoundException("parent class", parent.fqn); - //try to find by name - List<Element> candidates = parent.elem.getEnclosedElements() - .stream() - .filter(e -> (field && e instanceof VariableElement) || e instanceof ExecutableElement) - .filter(e -> e.getSimpleName().contentEquals(name)) - .collect(Collectors.toList()); - - if(candidates.size() == 0) - throw new TargetNotFoundException(field ? "field" : "method", name, parent.fqn); - - if(candidates.size() == 1 && (!strict || descr == null)) - return candidates.get(0); - - if(descr == null) { - throw new AmbiguousDefinitionException( - String.format("Found %d members named %s in class %s!", candidates.size(), name, parent.fqn) - ); - } else { - if(field) { - //fields can verify the signature for extra safety - //but there can only be 1 field with a given name - if(!descriptorFromType(candidates.get(0).asType(), env).equals(descr)) - throw new TargetNotFoundException("field", String.format("%s with descriptor %s", name, descr), parent.fqn); - } else { - candidates = candidates.stream() - .map(e -> (ExecutableElement) e) - .filter(strict - ? c -> descr.equals(descriptorFromExecutableElement(c, env)) - : c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c, env).split("\\)")[0]) - ).collect(Collectors.toList()); - } - if(candidates.size() == 0) - throw new TargetNotFoundException("method", String.format("%s %s", name, descr), parent.fqn); - if(candidates.size() > 1) - throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, parent.fqn) - ); - return candidates.get(0); - } - } - - /** - * Tries to find the method being overloaded by the given {@link ExecutableElement}. - * In case of multiple layers of overloading, it finds the original one. In case of - * no overloading, it returns the given method. - * @param context the {@link TypeElement} representing the parent class - * @param method an {@link ExecutableElement} representing the overloading method - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return the original overloaded method, or the given method if it was not found - * @since 0.5.2 - */ - public static ExecutableElement findOverloadedMethod( - TypeElement context, ExecutableElement method, ProcessingEnvironment env) { - if (context.getSuperclass().getKind() == TypeKind.NONE) - return method; - - for (Element elem : context.getEnclosedElements()) { - if (elem.getKind() != ElementKind.METHOD) - continue; - if (env.getElementUtils().overrides(method, (ExecutableElement) elem, context)) { - method = (ExecutableElement) elem; - break; //found - } - } - - return findOverloadedMethod( - (TypeElement) env.getTypeUtils().asElement(context.getSuperclass()), - method, env - ); - } - - /** - * Tries to find the "synthetic bridge" generated by the compiler for a certain overridden - * method. A "bridge" only exists in cases where type erasure is involved (i.e. when the - * method being overridden uses a generic parameter that is not preserved in the overriding - * method). - * @param context the {@link TypeElement} representing the parent class - * @param method an {@link ExecutableElement} stub representing the overloading method - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return the "bridge" - * @throws TargetNotFoundException if the method in question was not overriding anything, or - * if the method it was overriding does not require a bridge - * @since 0.5.2 - */ - public static ExecutableElement findSyntheticBridge( - TypeElement context, ExecutableElement method, ProcessingEnvironment env) throws TargetNotFoundException { - ExecutableElement overridding = findOverloadedMethod(context, method, env); - if(descriptorFromExecutableElement(overridding, env).equals(descriptorFromExecutableElement(method, env))) - throw new TargetNotFoundException( - "bridge method for", - overridding.getSimpleName().toString(), - context.getQualifiedName().toString() - ); - else return overridding; - } - - /** - * Utility method for finding out what type of proxy a field is. - * It will fail if the return type is not a known type of proxy. - * @param v the annotated {@link VariableElement} - * @return the {@link ProxyType} for the element - * @throws NotAProxyException if it's neither - * @since 0.4.0 - */ - public static ProxyType getProxyType(VariableElement v) { - String returnTypeFQN = v.asType().toString(); - switch(returnTypeFQN) { - case "ftbsc.lll.proxies.impl.FieldProxy": - return ProxyType.FIELD; - case "ftbsc.lll.proxies.impl.MethodProxy": - return ProxyType.METHOD; - case "ftbsc.lll.proxies.impl.TypeProxy": - return ProxyType.TYPE; - case "ftbsc.lll.proxies.impl.PackageProxy": - return ProxyType.PACKAGE; - default: - throw new NotAProxyException(v.getEnclosingElement().getSimpleName().toString(), v.getSimpleName().toString()); - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java b/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java deleted file mode 100644 index 77bbc2b..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java +++ /dev/null @@ -1,170 +0,0 @@ -package ftbsc.lll.processor.tools; - -import com.squareup.javapoet.*; -import ftbsc.lll.processor.annotations.Find; -import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.containers.ClassContainer; -import ftbsc.lll.processor.tools.containers.FieldContainer; -import ftbsc.lll.processor.tools.containers.InjectorInfo; -import ftbsc.lll.processor.tools.containers.MethodContainer; -import ftbsc.lll.proxies.ProxyType; -import ftbsc.lll.proxies.impl.FieldProxy; -import ftbsc.lll.proxies.impl.MethodProxy; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.*; -import java.util.HashSet; - -import static ftbsc.lll.processor.tools.ASTUtils.getProxyType; -import static ftbsc.lll.processor.tools.ASTUtils.mapModifiers; - -/** - * 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(); - } - - /** - * Appends to a given {@link MethodSpec.Builder} definitions for a proxy. - * @param var the {@link VariableElement} representing the proxy - * @param stub the stub {@link ExecutableElement} if present or relevant, null otherwise - * @param t the {@link Target} relevant to this finder if present or relevant, null otherwise - * @param con the {@link MethodSpec.Builder} to append to - * @param options the {@link ProcessorOptions} to be used - * @since 0.5.0 - */ - public static void appendMemberFinderDefinition( - VariableElement var, ExecutableElement stub, Target t, - MethodSpec.Builder con, ProcessorOptions options) { - ProxyType type = getProxyType(var); - if(type != ProxyType.METHOD && type != ProxyType.FIELD) - return; //this method is irrelevant to everyone else - - //we need this stuff - Find f = var.getAnnotation(Find.class); - final boolean isMethod = type == ProxyType.METHOD; - final String builderName = var.getSimpleName().toString() + "Builder"; - - String descriptorObf, nameObf; - ClassContainer parent; - Element target; - - if(isMethod) { - MethodContainer mc = MethodContainer.from(stub, t, f, options); - descriptorObf = mc.descriptorObf; - nameObf = mc.nameObf; - parent = mc.parent; - target = mc.elem; - } else { - FieldContainer fc = FieldContainer.from(var, options); - descriptorObf = fc.descriptorObf; - nameObf = fc.nameObf; - parent = fc.parent; - target = fc.elem; - } - - //initialize builder - con.addStatement("$T $L = $T.builder($S)", - isMethod ? MethodProxy.Builder.class : FieldProxy.Builder.class, - builderName, //variable name is always unique by definition - isMethod ? MethodProxy.class : FieldProxy.class, - nameObf - ); - - //set parent - con.addStatement( - "$L.setParent($S, $L)", - builderName, - parent.fqnObf, - parent.elem == null ? 0 : mapModifiers(parent.elem.getModifiers()) - ); - - //set modifiers - con.addStatement( - "$L.setModifiers($L)", - builderName, - target == null ? 0 : mapModifiers(target.getModifiers()) - ); - - //set type(s) - con.addStatement( - "$L.setDescriptor($S)", - builderName, - descriptorObf - ); - - //build and set - con.addStatement( - "super.$L = $L.build()", - var.getSimpleName().toString(), - builderName - ); - } - - /** - * Generates a {@link HashSet} of dummy overrides for every abstract method in a given class, - * represented as a {@link TypeElement}. - * @param clazz the given class - * @return a {@link HashSet} containing the generated {@link MethodSpec}s - * @since 0.5.0 - */ - public static HashSet<MethodSpec> generateDummies(TypeElement clazz) { - HashSet<MethodSpec> specs = new HashSet<>(); - clazz - .getEnclosedElements() - .stream() - .filter(e -> e instanceof ExecutableElement) - .map(e -> (ExecutableElement) e) - .forEach(e -> { - if(e.getModifiers().contains(Modifier.ABSTRACT)) - specs.add(MethodSpec.overriding(e) - .addStatement("throw new $T($S)", RuntimeException.class, "This is a stub and should not have been called") - .build() - ); - }); - return specs; - } - - /** - * Generates the wrapper around a certain injector. - * @param inj the {@link InjectorInfo} carrying the information about the target injector - * @param env the {@link ProcessingEnvironment} to perform the operation in - * @return the generated {@link MethodSpec} for the injector - * @since 0.6.0 - */ - public static MethodSpec generateInjector(InjectorInfo inj, ProcessingEnvironment env) { - MethodSpec.Builder injectBuilder = MethodSpec.methodBuilder("inject") - .addModifiers(Modifier.PUBLIC) - .returns(void.class) - .addAnnotation(Override.class) - .addParameter(ParameterSpec.builder( - TypeName.get(env - .getElementUtils() - .getTypeElement("org.objectweb.asm.tree.ClassNode").asType()), "clazz") - .build()) - .addParameter(ParameterSpec.builder( - TypeName.get(env - .getElementUtils() - .getTypeElement("org.objectweb.asm.tree.MethodNode").asType()), "main") - .build()); - - if(inj.injector.getParameters().size() == 2) - injectBuilder.addStatement("super.$L(clazz, main)", inj.injector.getSimpleName()); - else injectBuilder.addStatement("super.$L(main)", inj.injector.getSimpleName()); - - return injectBuilder.build(); - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java b/src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java deleted file mode 100644 index 0ef8a18..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java +++ /dev/null @@ -1,107 +0,0 @@ -package ftbsc.lll.processor.tools; - -import ftbsc.lll.IInjector; -import ftbsc.lll.exceptions.InvalidResourceException; -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; - -import javax.annotation.processing.ProcessingEnvironment; -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * Class in charge of containing, parsing and processing all processor options, - * from the simpler booleans to the more complicated mapper. - */ -public class ProcessorOptions { - - /** - * A {@link Set} of options currently supported by the processor. - */ - public static final Set<String> SUPPORTED = new HashSet<>(Arrays.asList( - "mappingsFile", "anonymousClassWarning", "obfuscateInjectorMetadata", - "noServiceProvider" - )); - - /** - * The environment the processor is acting in. - */ - public final ProcessingEnvironment env; - - /** - * The {@link ObfuscationMapper} used to convert classes and variables - * to their obfuscated equivalent. Will be null when no mapper is in use. - */ - public final ObfuscationMapper mapper; - - /** - * Whether the processor should issue warnings when compiling code anonymous - * classes which can't be checked for validity. - */ - public final boolean anonymousClassWarning; - - /** - * Whether injector metadata (what is returned by the functions of {@link IInjector}) - * is to use obfuscated names instead of its normal names. - */ - public final boolean obfuscateInjectorMetadata; - - /** - * Whether the processor should skip the generation of the service provider. - */ - public final boolean noServiceProvider; - - /** - * The public constructor, parses and stores all given arguments. - * @param env the environment the processor is working in - */ - public ProcessorOptions(ProcessingEnvironment env) { - this.env = env; - String location = env.getOptions().get("mappingsFile"); - if(location == null) - this.mapper = null; - else { - InputStream targetStream; - try { - 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()); - } - this.anonymousClassWarning = parseBooleanArg(env.getOptions().get("anonymousClassWarning"), true); - this.obfuscateInjectorMetadata = parseBooleanArg(env.getOptions().get("obfuscateInjectorMetadata"), true); - this.noServiceProvider = parseBooleanArg(env.getOptions().get("noServiceProvider"), false); - } - - /** - * Parses a boolean arg from a String. - * @param arg the arg to parse - * @return the parsed boolean - */ - private static boolean parseBooleanArg(String arg, boolean defaultValue) { - if(arg == null) return defaultValue; - try { // 0 = false, any other integer = true - int i = Integer.parseInt(arg); - return i != 0; - } catch(NumberFormatException ignored) { - return Boolean.parseBoolean(arg); - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java b/src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java deleted file mode 100644 index 2c10dd6..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java +++ /dev/null @@ -1,136 +0,0 @@ -package ftbsc.lll.processor.tools.containers; - -import ftbsc.lll.exceptions.TargetNotFoundException; -import ftbsc.lll.processor.annotations.Find; -import ftbsc.lll.processor.annotations.Patch; -import ftbsc.lll.processor.tools.ProcessorOptions; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; -import java.lang.annotation.Annotation; -import java.util.function.Function; - -import static ftbsc.lll.processor.tools.ASTUtils.*; - -/** - * Container for information about a class. - * Used internally for efficiency reasons. - * @since 0.5.0 - */ -public class ClassContainer { - /** - * The fully-qualified name of the class. - */ - public final String fqn; - - /** - * The obfuscated fully-qualified name of the class. - * If the mapper passed is null, then this will be identical to {@link #fqn} - */ - public final String fqnObf; - - /** - * The {@link Element} corresponding to the class. - * May only be null intentionally i.e. when the associated element is - * an anonymous class or a child of an anonymous class. - */ - public final Element elem; - - /** - * Private constructor, called from {@link #from(Annotation, Function, String, ProcessorOptions)}. - * @param fqn the fully-qualified name of the target class - * @param innerNames an array of Strings containing the path to the inner class, may be null - * @param options the {@link ProcessorOptions} to be used - */ - private ClassContainer(String fqn, String[] innerNames, ProcessorOptions options) { - //find and validate - Element elem = options.env.getElementUtils().getTypeElement(fqn); - - if(elem == null) - throw new TargetNotFoundException("class", fqn); - - StringBuilder fqnBuilder = new StringBuilder( - internalNameFromType(elem.asType(), options.env).replace('/', '.') - ); - - if(innerNames != null) { - for(String inner : innerNames) { - if(inner == null) continue; - fqnBuilder.append("$").append(inner); - try { - int anonClassCounter = Integer.parseInt(inner); - //anonymous classes cannot be validated! - if(options.anonymousClassWarning) - options.env.getMessager().printMessage( - Diagnostic.Kind.WARNING, - String.format( - "Anonymous classes cannot be verified by the processor. The existence of %s$%s is not guaranteed!", - fqnBuilder, anonClassCounter - ) - ); - elem = null; - break; - } catch(NumberFormatException exc) { - elem = elem - .getEnclosedElements() - .stream() - .filter(e -> e instanceof TypeElement) - .filter(e -> e.getSimpleName().contentEquals(inner)) - .findFirst() - .orElse(null); - } - if(elem == null) - throw new TargetNotFoundException("class", inner); - } - } - this.fqn = fqnBuilder.toString(); - this.fqnObf = findClassName(this.fqn, options); - this.elem = elem; - } - - /** - * Safely extracts a {@link Class} from an annotation and gets its fully qualified name. - * @param ann the annotation containing the class - * @param classFunction the annotation function returning the class - * @param innerName a string containing the inner class name or nothing - * @param options the {@link ProcessorOptions} to be used - * @param <T> the type of the annotation carrying the information - * @return the fully qualified name of the given class - * @since 0.5.0 - */ - public static <T extends Annotation> ClassContainer from(T ann, Function<T, Class<?>> classFunction, String innerName, ProcessorOptions options) { - String fqn; - String[] inner; - fqn = getTypeFromAnnotation(ann, classFunction, options.env).toString(); - inner = innerName.equals("") ? null : innerName.split("//$"); - return new ClassContainer(fqn, inner, options); - } - - /** - * Safely extracts a {@link Class} from an annotation and gets its fully qualified name. - * @param cl the {@link TypeElement} representing the class - * @param options the {@link ProcessorOptions} to be used - * @return the fully qualified name of the given class - * @since 0.6.0 - */ - public static ClassContainer from(TypeElement cl, ProcessorOptions options) { - return new ClassContainer(cl.getQualifiedName().toString(), null, options); - } - - /** - * Finds and builds a {@link ClassContainer} based on information contained - * within {@link Patch} or a {@link Find} annotations, else returns a fallback. - * @param fallback the {@link ClassContainer} it falls back on - * @param p the {@link Patch} annotation to get info from - * @param f the {@link Find} annotation to get info from - * @param options the {@link ProcessorOptions} to be used - * @return the built {@link ClassContainer} or the fallback if not enough information was present - * @since 0.5.0 - */ - public static ClassContainer findOrFallback(ClassContainer fallback, Patch p, Find f, ProcessorOptions options) { - if(f == null) return ClassContainer.from(p, Patch::value, p.innerName(), options); - ClassContainer cl = ClassContainer.from(f, Find::value, f.innerName(), options); - return cl.fqn.equals("java.lang.Object") ? fallback : cl; - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java b/src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java deleted file mode 100644 index 2dd2ecb..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java +++ /dev/null @@ -1,113 +0,0 @@ -package ftbsc.lll.processor.tools.containers; - -import ftbsc.lll.exceptions.AmbiguousDefinitionException; -import ftbsc.lll.processor.annotations.Find; -import ftbsc.lll.processor.annotations.Patch; -import ftbsc.lll.processor.tools.ProcessorOptions; -import org.objectweb.asm.Type; - -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; - -import static ftbsc.lll.processor.tools.ASTUtils.*; - -/** - * Container for information about a field. - * Used internally for efficiency reasons. - * @since 0.5.0 - */ -public class FieldContainer { - /** - * The name of the field. - */ - public final String name; - - /** - * The descriptor of the field. - */ - public final String descriptor; - - /** - * The obfuscated name of the field. - * If the mapper passed is null, then this will be identical to {@link #name}. - */ - public final String nameObf; - - /** - * The obfuscated descriptor of the field. - * If the mapper passed is null, then this will be identical to {@link #descriptor}. - */ - public final String descriptorObf; - - /** - * The {@link ClassContainer} representing the parent of this field. - */ - public final ClassContainer parent; - - /** - * The {@link VariableElement} corresponding to the field. - * May only be null intentionally i.e. when the field is - * a child of an anonymous class. - */ - public final VariableElement elem; - - /** - * Private constructor, called from {@link #from(VariableElement, ProcessorOptions)}. - * @param parent the {@link ClassContainer} representing the parent - * @param name the fully-qualified name of the target field - * @param descriptor the descriptor of the target field, may be null for verifiable fields - * @param options the {@link ProcessorOptions} to be used - */ - private FieldContainer(ClassContainer parent, String name, String descriptor, ProcessorOptions options) { - this.parent = parent; - if(parent.elem == null) { //unverified - if(descriptor == null) - throw new AmbiguousDefinitionException("Cannot use name-based lookups for fields of unverifiable classes!"); - this.elem = null; - this.name = name; - this.descriptor = descriptor; - } else { - this.elem = (VariableElement) findMember(parent, name, descriptor, descriptor != null, true, options.env); - this.name = this.elem.getSimpleName().toString(); - this.descriptor = descriptorFromType(this.elem.asType(), options.env); - } - this.descriptorObf = options.mapper == null ? this.descriptor : options.mapper.obfuscateType(Type.getType(this.descriptor)).getDescriptor(); - this.nameObf = findMemberName(parent.fqn, this.name, null, options.mapper); - } - - /** - * Finds a {@link FieldContainer} from a finder. - * @param finder the {@link VariableElement} annotated with {@link Find} for this field - * @param options the {@link ProcessorOptions} to be used - * @return the built {@link FieldContainer} - * @since 0.5.0 - */ - public static FieldContainer from(VariableElement finder, ProcessorOptions options) { - //the parent always has a @Patch annotation - Patch patchAnn = finder.getEnclosingElement().getAnnotation(Patch.class); - //the finder always has a @Find annotation - Find f = finder.getAnnotation(Find.class); - - ClassContainer parent = ClassContainer.findOrFallback( - ClassContainer.from((TypeElement) finder.getEnclosingElement(), options), patchAnn, f, options - ); - - String name = f.name().equals("") ? finder.getSimpleName().toString() : f.name(); - String descriptor; - TypeMirror fieldType = getTypeFromAnnotation(f, Find::type, options.env); - if(fieldType.toString().equals("java.lang.Object")) { - descriptor = null; - } else { - if(fieldType.getKind() != TypeKind.VOID && !fieldType.getKind().isPrimitive()) - descriptor = //jank af but this is temporary anyway - "L" + ClassContainer.from( - f, Find::type, f.typeInner(), options - ).fqnObf.replace('.', '/') + ";"; - else descriptor = descriptorFromType(fieldType, options.env); - } - - return new FieldContainer(parent, name, descriptor, options); - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java b/src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java deleted file mode 100644 index 460401b..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java +++ /dev/null @@ -1,46 +0,0 @@ -package ftbsc.lll.processor.tools.containers; - -import ftbsc.lll.processor.annotations.Injector; -import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.ProcessorOptions; - -import javax.lang.model.element.ExecutableElement; - -/** - * Container for information about a class that is to be generated. - */ -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 reason for the injection. - */ - public final String reason; - - /** - * The {@link MethodContainer} corresponding to the target method. - */ - public final MethodContainer target; - - /** - * Public constructor. - * @param injector the injector {@link ExecutableElement} - * @param targetStub the target {@link ExecutableElement} - * @param targetAnn the relevant {@link Target} annotation - * @param options the {@link ProcessorOptions} to be used - */ - public InjectorInfo(ExecutableElement injector, ExecutableElement targetStub, Target targetAnn, ProcessorOptions options) { - this.injector = injector; - this.targetStub = targetStub; - this.reason = injector.getAnnotation(Injector.class).reason(); - this.target = MethodContainer.from(targetStub, targetAnn, null, options); - } -}
\ No newline at end of file diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java b/src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java deleted file mode 100644 index 714ee54..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java +++ /dev/null @@ -1,111 +0,0 @@ -package ftbsc.lll.processor.tools.containers; - -import ftbsc.lll.exceptions.AmbiguousDefinitionException; -import ftbsc.lll.exceptions.TargetNotFoundException; -import ftbsc.lll.processor.annotations.Find; -import ftbsc.lll.processor.annotations.Patch; -import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.ProcessorOptions; - -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; - -import static ftbsc.lll.processor.tools.ASTUtils.*; - -/** - * Container for information about a method. - * Used internally for efficiency reasons. - * @since 0.5.0 - */ -public class MethodContainer { - /** - * The name of the method. - */ - public final String name; - - /** - * The descriptor of the method. - */ - public final String descriptor; - - /** - * The obfuscated name of the method. - * If the mapper passed is null, then this will be identical to {@link #name}. - */ - public final String nameObf; - - /** - * The obfuscated descriptor of the field. - * If the mapper passed is null, then this will be identical to {@link #descriptor}. - */ - public final String descriptorObf; - - /** - * The {@link ClassContainer} representing the parent of this method. - */ - public final ClassContainer parent; - - /** - * The {@link ExecutableElement} corresponding to the method. - * May only be null intentionally i.e. when the method is - * a child of an anonymous class. - */ - public final ExecutableElement elem; - - /** - * Private constructor, called from - * {@link #from(ExecutableElement, Target, Find, ProcessorOptions)}. - * @param parent the {@link ClassContainer} representing the parent - * @param name the fully-qualified name of the target method - * @param descriptor the descriptor of the target method - * @param strict whether the matching should be strict (see {@link Target#strict()} for more info) - * @param bridge whether the "bridge" should be matched instead (see {@link Target#bridge()} for more info) - * @param options the {@link ProcessorOptions} to be used - */ - private MethodContainer(ClassContainer parent, String name, String descriptor, boolean strict, boolean bridge, ProcessorOptions options) { - this.parent = parent; - if(parent.elem == null) { //unverified - if(descriptor == null) - throw new AmbiguousDefinitionException("Cannot use name-based lookups for methods of unverifiable classes!"); - this.elem = null; - this.name = name; - this.descriptor = descriptor; - } else { - ExecutableElement tmp = (ExecutableElement) findMember( - parent, name, descriptor, descriptor != null && strict,false, options.env - ); - this.elem = bridge ? findSyntheticBridge((TypeElement) this.parent.elem, tmp, options.env) : tmp; - this.name = this.elem.getSimpleName().toString(); - this.descriptor = descriptorFromExecutableElement(this.elem, options.env); - } - this.descriptorObf = options.mapper == null ? this.descriptor : options.mapper.obfuscateMethodDescriptor(this.descriptor); - this.nameObf = findMemberName(parent.fqn, this.name, this.descriptor, options.mapper); - } - - /** - * Builds the {@link MethodContainer} corresponding to a stub annotated with {@link Target}. - * @param stub the {@link ExecutableElement} for the stub - * @param t the {@link Target} annotation relevant to this case - * @param f the {@link Find} annotation containing fallback data, may be null - * @param options the {@link ProcessorOptions} to be used - * @return the {@link MethodContainer} corresponding to the method - * @throws AmbiguousDefinitionException if it finds more than one candidate - * @throws TargetNotFoundException if it finds no valid candidate - * @since 0.3.0 - */ - public static MethodContainer from(ExecutableElement stub, Target t, Find f, ProcessorOptions options) { - //the parent always has a @Patch annotation - Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class); - ClassContainer parent = ClassContainer.findOrFallback( - ClassContainer.from((TypeElement) stub.getEnclosingElement(), options), patchAnn, f, options - ); - String name = !t.methodName().equals("") - ? t.methodName() //name was specified in target - : stub.getSimpleName().toString(); - String descriptor = t.strict() - ? descriptorFromExecutableElement(stub, options.env) - : null; - - return new MethodContainer(parent, name, descriptor, t.strict(), t.bridge(), options); - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java b/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java deleted file mode 100644 index 5938de3..0000000 --- a/src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java +++ /dev/null @@ -1,225 +0,0 @@ -package ftbsc.lll.processor.tools.obfuscation; - -import ftbsc.lll.exceptions.AmbiguousDefinitionException; -import ftbsc.lll.exceptions.MappingNotFoundException; -import ftbsc.lll.tools.DescriptorBuilder; -import org.objectweb.asm.Type; - -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; - -/** - * 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<String, ObfuscationData> 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<String> str) { - AtomicReference<String> currentClass = new AtomicReference<>(""); - str.forEach(l -> { - if(l == null) return; - 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.replace('.', '/')); - if(data == null) - throw new MappingNotFoundException(name); - else return data.obf; - } - - /** - * 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 - * @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, String methodDescriptor) { - ObfuscationData data = mapper.get(parentName.replace('.', '/')); - if(data == null) - throw new MappingNotFoundException(parentName + "::" + memberName); - return data.get(memberName, methodDescriptor); - } - - /** - * Obfuscates a method descriptor, replacing its class references - * with their obfuscated counterparts. - * @param descriptor a {@link String} containing the descriptor - * @return the obfuscated descriptor - * @since 0.5.1 - */ - public String obfuscateMethodDescriptor(String descriptor) { - Type method = Type.getMethodType(descriptor); - Type[] arguments = method.getArgumentTypes(); - Type returnType = method.getReturnType(); - - Type[] obfArguments = new Type[arguments.length]; - for(int i = 0; i < obfArguments.length; i++) - obfArguments[i] = this.obfuscateType(arguments[i]); - - return Type.getMethodDescriptor(this.obfuscateType(returnType), obfArguments); - } - - /** - * Given a {@link Type} it returns its obfuscated counterpart. - * @param type the type in question - * @return the obfuscated type - * @since 0.5.1 - */ - public Type obfuscateType(Type type) { - //unwrap arrays - Type unwrapped = type; - int arrayLevel = 0; - while(unwrapped.getSort() == org.objectweb.asm.Type.ARRAY) { - unwrapped = unwrapped.getElementType(); - arrayLevel++; - } - - //if it's a primitive no operation is needed - if(type.getSort() < org.objectweb.asm.Type.ARRAY) - return type; - - String internalName = type.getInternalName(); - - String internalNameObf; - try { - internalNameObf = this.obfuscateClass(internalName); - return Type.getType(DescriptorBuilder.nameToDescriptor(internalNameObf, arrayLevel)); - } catch(MappingNotFoundException e) { - return type; - } - } - - /** - * 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<String, String> 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]); - } - - /** - * 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) { - - //find all keys that start with the name - List<String> candidates = members.keySet().stream().filter( - m -> m.split(" ")[0].equals(memberName) - ).collect(Collectors.toList()); - - if(methodDescriptor != null) { - String signature = String.format("%s %s", memberName, methodDescriptor); - candidates = candidates.stream().filter( - m -> m.equals(signature) - ).collect(Collectors.toList()); - } - - switch(candidates.size()) { - case 0: - throw new MappingNotFoundException(String.format( - "%s.%s%s", - this.unobf, - memberName, - methodDescriptor == null ? "" : "()" - )); - case 1: - return members.get(candidates.get(0)); - default: - throw new AmbiguousDefinitionException(String.format( - "Mapper could not uniquely identify member %s.%s%s, found %d!", - this.unobf, - memberName, - methodDescriptor == null ? "" : "()", - candidates.size() - )); - } - } - } -} |