aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/processor/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/ftbsc/lll/processor/tools')
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/ASTUtils.java381
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java170
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java107
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java136
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java113
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java46
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java111
-rw-r--r--src/main/java/ftbsc/lll/processor/tools/obfuscation/ObfuscationMapper.java225
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()
- ));
- }
- }
- }
-}