diff --git a/build.gradle b/build.gradle index 0bebd2c..6ecc5eb 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } archivesBaseName = 'processor' -version = gitVersion().split('-').getAt(0).replace('dirty', '') +version = versionDetails().lastTag java { sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 @@ -27,5 +27,6 @@ repositories { dependencies { implementation 'com.squareup:javapoet:1.13.0' implementation 'org.ow2.asm:asm-commons:9.5' - implementation 'ftbsc:lll:0.4.1' + implementation 'ftbsc:lll:0.5.0' + implementation 'ftbsc.lll:mapper:0.3.0' } diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java deleted file mode 100644 index e943c01..0000000 --- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java +++ /dev/null @@ -1,27 +0,0 @@ -package ftbsc.lll.exceptions; - -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; - -/** - * Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}. - */ -public class MappingNotFoundException extends RuntimeException { - - /** - * Constructs a new mapping not found exception for the specified mapping. - * @param mapping the relevant mapping - */ - public MappingNotFoundException(String mapping) { - super(String.format("Could not find mapping for %s!", mapping)); - } - - /** - * Constructs a new mapping not found exception for the specified mapping - * with the specified reason. - * @param mapping the relevant mapping - * @param reason the reason message - */ - public MappingNotFoundException(String mapping, String reason) { - this(mapping + ": " + reason); - } -} diff --git a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java index b1d1f1e..b693592 100644 --- a/src/main/java/ftbsc/lll/processor/LilleroProcessor.java +++ b/src/main/java/ftbsc/lll/processor/LilleroProcessor.java @@ -8,10 +8,9 @@ import ftbsc.lll.IInjector; import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.OrphanElementException; import ftbsc.lll.processor.annotations.*; -import ftbsc.lll.processor.tools.ProcessorOptions; -import ftbsc.lll.processor.tools.containers.ClassContainer; -import ftbsc.lll.processor.tools.containers.InjectorInfo; -import ftbsc.lll.processor.tools.containers.MethodContainer; +import ftbsc.lll.processor.containers.ClassContainer; +import ftbsc.lll.processor.containers.InjectorInfo; +import ftbsc.lll.processor.containers.MethodContainer; import ftbsc.lll.proxies.ProxyType; import ftbsc.lll.proxies.impl.TypeProxy; @@ -29,8 +28,8 @@ import java.io.PrintWriter; import java.util.*; import java.util.stream.Collectors; -import static ftbsc.lll.processor.tools.ASTUtils.*; -import static ftbsc.lll.processor.tools.JavaPoetUtils.*; +import static ftbsc.lll.processor.utils.ASTUtils.*; +import static ftbsc.lll.processor.utils.JavaPoetUtils.*; /** * The actual annotation processor behind the magic. @@ -48,7 +47,7 @@ public class LilleroProcessor extends AbstractProcessor { /** * An object representing the various options passed to the processor. */ - public final ProcessorOptions options = new ProcessorOptions(processingEnv); + private ProcessorOptions options = null; /** * Method overriding default implementation to manually pass supported options. @@ -59,6 +58,16 @@ public class LilleroProcessor extends AbstractProcessor { return ProcessorOptions.SUPPORTED; } + /** + * Returns the {@link ProcessorOptions} for this instance, creating the object if + * it hasn't been already. + * @return the {@link ProcessorOptions} for this instance + */ + public ProcessorOptions getProcessorOptions() { + if(this.options == null) this.options = new ProcessorOptions(this.processingEnv); + return this.options; + } + /** * Where the actual processing happens. * It filters through whatever annotated class it's fed, and checks whether it contains @@ -90,7 +99,7 @@ public class LilleroProcessor extends AbstractProcessor { } } } - if (!this.options.noServiceProvider && !this.injectors.isEmpty()) { + if (!this.getProcessorOptions().noServiceProvider && !this.injectors.isEmpty()) { generateServiceProvider(); return true; } else return false; @@ -134,7 +143,7 @@ public class LilleroProcessor extends AbstractProcessor { //find class information Patch patchAnn = cl.getAnnotation(Patch.class); ClassContainer targetClass = ClassContainer.from( - patchAnn, Patch::value, patchAnn.innerName(), this.options + patchAnn, Patch::value, patchAnn.innerName(), this.getProcessorOptions() ); //find package information Element packageElement = cl.getEnclosingElement(); @@ -156,7 +165,7 @@ public class LilleroProcessor extends AbstractProcessor { //take care of TypeProxies and FieldProxies first for(VariableElement proxyVar : finders) { ProxyType type = getProxyType(proxyVar); - if(type == ProxyType.METHOD && proxyVar.getAnnotation(Find.class).name().equals("")) { + if(type == ProxyType.METHOD && proxyVar.getAnnotation(Find.class).name().isEmpty()) { //methods without a specified name will be handled later methodFinders.add(proxyVar); continue; @@ -165,17 +174,17 @@ public class LilleroProcessor extends AbstractProcessor { if(type == ProxyType.TYPE) { //find and validate ClassContainer clazz = ClassContainer.findOrFallback( - ClassContainer.from(cl, this.options), + ClassContainer.from(cl, this.getProcessorOptions()), patchAnn, proxyVar.getAnnotation(Find.class), - this.options + this.getProcessorOptions() ); //types can be generated with a single instruction constructorBuilder.addStatement( "super.$L = $T.from($S, 0, $L)", proxyVar.getSimpleName().toString(), TypeProxy.class, - clazz.fqnObf, //use obf name, at runtime it will be obfuscated + clazz.data.nameMapped.replace('/', '.'), //use obf name, at runtime it will be obfuscated clazz.elem == null ? 0 : mapModifiers(clazz.elem.getModifiers()) ); } else if(type == ProxyType.FIELD) @@ -184,7 +193,7 @@ public class LilleroProcessor extends AbstractProcessor { null, null, constructorBuilder, - this.options + this.getProcessorOptions() ); } @@ -215,11 +224,11 @@ public class LilleroProcessor extends AbstractProcessor { .collect(Collectors.toList()); //throw exception if user is a moron and defined a finder and an injector with the same name - if(finderCandidates.size() != 0 && injectorCandidates.size() != 0) + if(!finderCandidates.isEmpty() && !injectorCandidates.isEmpty()) throw new AmbiguousDefinitionException( String.format("Target specified user %s, but name was used by both a finder and injector.", targetAnn.of()) ); - else if(finderCandidates.size() == 0 && injectorCandidates.size() == 0) + else if(finderCandidates.isEmpty() && injectorCandidates.isEmpty()) processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, String.format( "Found orphan @Target annotation on method %s.%s pointing at method %s, it will be ignored!", @@ -228,11 +237,11 @@ public class LilleroProcessor extends AbstractProcessor { targetAnn.of() ) ); - else if(finderCandidates.size() == 0 && injectorCandidates.size() != 1) + else if(finderCandidates.isEmpty() && injectorCandidates.size() != 1) throw new AmbiguousDefinitionException( String.format("Found multiple candidate injectors for target %s::%s!", cl.getSimpleName(), tg.getSimpleName()) ); - else if(injectorCandidates.size() == 0 && finderCandidates.size() != 1) + else if(injectorCandidates.isEmpty() && finderCandidates.size() != 1) throw new AmbiguousDefinitionException( String.format("Found multiple candidate finders for target %s::%s!", cl.getSimpleName(), tg.getSimpleName()) ); @@ -243,7 +252,7 @@ public class LilleroProcessor extends AbstractProcessor { matchedInjectors.add(injector); toGenerate.put( String.format("%sInjector%d", cl.getSimpleName(), iterationNumber), - new InjectorInfo(injector, tg, targetAnn, this.options) + new InjectorInfo(injector, tg, targetAnn, this.getProcessorOptions()) ); iterationNumber++; //increment is only used by injectors } else { @@ -255,7 +264,7 @@ public class LilleroProcessor extends AbstractProcessor { tg, targetAnn, constructorBuilder, - this.options + this.getProcessorOptions() ); } } @@ -280,9 +289,13 @@ public class LilleroProcessor extends AbstractProcessor { .addMethod(constructorBuilder.build()) .addMethod(buildStringReturnMethod("name", injName)) .addMethod(buildStringReturnMethod("reason", toGenerate.get(injName).reason)) - .addMethod(buildStringReturnMethod("targetClass", this.options.obfuscateInjectorMetadata ? targetClass.fqnObf : targetClass.fqn)) - .addMethod(buildStringReturnMethod("methodName", this.options.obfuscateInjectorMetadata ? target.nameObf : target.name)) - .addMethod(buildStringReturnMethod("methodDesc", this.options.obfuscateInjectorMetadata ? target.descriptorObf : target.descriptor)) + .addMethod(buildStringReturnMethod("targetClass", this.getProcessorOptions().obfuscateInjectorMetadata + ? targetClass.data.nameMapped.replace('/', '.') + : targetClass.data.name.replace('/', '.'))) + .addMethod(buildStringReturnMethod("methodName", this.getProcessorOptions().obfuscateInjectorMetadata + ? target.data.nameMapped : target.data.signature.name)) + .addMethod(buildStringReturnMethod("methodDesc", this.getProcessorOptions().obfuscateInjectorMetadata + ? target.descriptorObf : target.data.signature.name)) .addMethods(generateDummies(cl)) .addMethod(generateInjector(toGenerate.get(injName), this.processingEnv)) .build(); diff --git a/src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java b/src/main/java/ftbsc/lll/processor/ProcessorOptions.java similarity index 67% rename from src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java rename to src/main/java/ftbsc/lll/processor/ProcessorOptions.java index 0ef8a18..5972631 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ProcessorOptions.java +++ b/src/main/java/ftbsc/lll/processor/ProcessorOptions.java @@ -1,16 +1,13 @@ -package ftbsc.lll.processor.tools; +package ftbsc.lll.processor; import ftbsc.lll.IInjector; -import ftbsc.lll.exceptions.InvalidResourceException; -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; +import ftbsc.lll.mapper.MapperProvider; +import ftbsc.lll.mapper.tools.Mapper; 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.List; import java.util.Set; /** @@ -33,10 +30,10 @@ public class ProcessorOptions { public final ProcessingEnvironment env; /** - * The {@link ObfuscationMapper} used to convert classes and variables + * The {@link Mapper} used to convert classes and variables * to their obfuscated equivalent. Will be null when no mapper is in use. */ - public final ObfuscationMapper mapper; + public final Mapper mapper; /** * Whether the processor should issue warnings when compiling code anonymous @@ -62,29 +59,10 @@ public class ProcessorOptions { 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()); - } + if(location != null) { + List lines = MapperProvider.fetchFromLocalOrRemote(location); + this.mapper = MapperProvider.getMapper(lines).getMapper(lines, true); + } else this.mapper = null; this.anonymousClassWarning = parseBooleanArg(env.getOptions().get("anonymousClassWarning"), true); this.obfuscateInjectorMetadata = parseBooleanArg(env.getOptions().get("obfuscateInjectorMetadata"), true); this.noServiceProvider = parseBooleanArg(env.getOptions().get("noServiceProvider"), false); diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java b/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java similarity index 89% rename from src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java rename to src/main/java/ftbsc/lll/processor/containers/ClassContainer.java index 2c10dd6..297349a 100644 --- a/src/main/java/ftbsc/lll/processor/tools/containers/ClassContainer.java +++ b/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java @@ -1,9 +1,10 @@ -package ftbsc.lll.processor.tools.containers; +package ftbsc.lll.processor.containers; import ftbsc.lll.exceptions.TargetNotFoundException; +import ftbsc.lll.mapper.tools.data.ClassData; import ftbsc.lll.processor.annotations.Find; import ftbsc.lll.processor.annotations.Patch; -import ftbsc.lll.processor.tools.ProcessorOptions; +import ftbsc.lll.processor.ProcessorOptions; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; @@ -11,7 +12,7 @@ import javax.tools.Diagnostic; import java.lang.annotation.Annotation; import java.util.function.Function; -import static ftbsc.lll.processor.tools.ASTUtils.*; +import static ftbsc.lll.processor.utils.ASTUtils.*; /** * Container for information about a class. @@ -20,15 +21,9 @@ import static ftbsc.lll.processor.tools.ASTUtils.*; */ public class ClassContainer { /** - * The fully-qualified name of the class. + * The {@link ClassData} for the class represented by this container. */ - 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; + public final ClassData data; /** * The {@link Element} corresponding to the class. @@ -84,8 +79,7 @@ public class ClassContainer { throw new TargetNotFoundException("class", inner); } } - this.fqn = fqnBuilder.toString(); - this.fqnObf = findClassName(this.fqn, options); + this.data = getClassData(fqnBuilder.toString(), options.mapper); this.elem = elem; } @@ -131,6 +125,6 @@ public class ClassContainer { 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; + return cl.data.name.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/containers/FieldContainer.java similarity index 80% rename from src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java rename to src/main/java/ftbsc/lll/processor/containers/FieldContainer.java index 2dd2ecb..2299c93 100644 --- a/src/main/java/ftbsc/lll/processor/tools/containers/FieldContainer.java +++ b/src/main/java/ftbsc/lll/processor/containers/FieldContainer.java @@ -1,9 +1,11 @@ -package ftbsc.lll.processor.tools.containers; +package ftbsc.lll.processor.containers; import ftbsc.lll.exceptions.AmbiguousDefinitionException; +import ftbsc.lll.mapper.tools.MappingUtils; +import ftbsc.lll.mapper.tools.data.FieldData; import ftbsc.lll.processor.annotations.Find; import ftbsc.lll.processor.annotations.Patch; -import ftbsc.lll.processor.tools.ProcessorOptions; +import ftbsc.lll.processor.ProcessorOptions; import org.objectweb.asm.Type; import javax.lang.model.element.TypeElement; @@ -11,7 +13,7 @@ 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.*; +import static ftbsc.lll.processor.utils.ASTUtils.*; /** * Container for information about a field. @@ -20,21 +22,15 @@ import static ftbsc.lll.processor.tools.ASTUtils.*; */ public class FieldContainer { /** - * The name of the field. + * The {@link FieldData} for the field represented by this container. */ - public final String name; + public final FieldData data; /** * 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}. @@ -66,15 +62,15 @@ public class FieldContainer { 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); + name = this.elem.getSimpleName().toString(); } - 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); + this.data = getFieldData(parent.data.name, name, options.mapper); + this.descriptorObf = options.mapper == null ? this.descriptor + : MappingUtils.mapType(Type.getType(this.descriptor), options.mapper, false).getDescriptor(); } /** @@ -94,7 +90,7 @@ public class FieldContainer { ClassContainer.from((TypeElement) finder.getEnclosingElement(), options), patchAnn, f, options ); - String name = f.name().equals("") ? finder.getSimpleName().toString() : f.name(); + String name = f.name().isEmpty() ? finder.getSimpleName().toString() : f.name(); String descriptor; TypeMirror fieldType = getTypeFromAnnotation(f, Find::type, options.env); if(fieldType.toString().equals("java.lang.Object")) { @@ -104,7 +100,7 @@ public class FieldContainer { descriptor = //jank af but this is temporary anyway "L" + ClassContainer.from( f, Find::type, f.typeInner(), options - ).fqnObf.replace('.', '/') + ";"; + ).data.nameMapped + ";"; else descriptor = descriptorFromType(fieldType, options.env); } diff --git a/src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java b/src/main/java/ftbsc/lll/processor/containers/InjectorInfo.java similarity index 92% rename from src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java rename to src/main/java/ftbsc/lll/processor/containers/InjectorInfo.java index 460401b..d56d846 100644 --- a/src/main/java/ftbsc/lll/processor/tools/containers/InjectorInfo.java +++ b/src/main/java/ftbsc/lll/processor/containers/InjectorInfo.java @@ -1,8 +1,8 @@ -package ftbsc.lll.processor.tools.containers; +package ftbsc.lll.processor.containers; import ftbsc.lll.processor.annotations.Injector; import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.ProcessorOptions; +import ftbsc.lll.processor.ProcessorOptions; import javax.lang.model.element.ExecutableElement; @@ -43,4 +43,4 @@ public class InjectorInfo { 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/containers/MethodContainer.java similarity index 77% rename from src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java rename to src/main/java/ftbsc/lll/processor/containers/MethodContainer.java index 714ee54..7d15599 100644 --- a/src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java +++ b/src/main/java/ftbsc/lll/processor/containers/MethodContainer.java @@ -1,16 +1,18 @@ -package ftbsc.lll.processor.tools.containers; +package ftbsc.lll.processor.containers; import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.TargetNotFoundException; +import ftbsc.lll.mapper.tools.MappingUtils; +import ftbsc.lll.mapper.tools.data.MethodData; 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 ftbsc.lll.processor.ProcessorOptions; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; -import static ftbsc.lll.processor.tools.ASTUtils.*; +import static ftbsc.lll.processor.utils.ASTUtils.*; /** * Container for information about a method. @@ -19,24 +21,14 @@ import static ftbsc.lll.processor.tools.ASTUtils.*; */ public class MethodContainer { /** - * The name of the method. + * The {@link MethodData} for the method represented by this container. */ - 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; + public final MethodData data; /** * The obfuscated descriptor of the field. - * If the mapper passed is null, then this will be identical to {@link #descriptor}. + * If the mapper passed is null, this will be identical to the one inside + * {@link #data}. */ public final String descriptorObf; @@ -68,18 +60,17 @@ public class MethodContainer { 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); + name = this.elem.getSimpleName().toString(); + 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); + this.data = getMethodData(parent.data.name, name, descriptor, options.mapper); + this.descriptorObf = options.mapper == null ? this.data.signature.descriptor + : MappingUtils.mapMethodDescriptor(this.data.signature.descriptor, options.mapper, false); } /** @@ -99,7 +90,7 @@ public class MethodContainer { ClassContainer parent = ClassContainer.findOrFallback( ClassContainer.from((TypeElement) stub.getEnclosingElement(), options), patchAnn, f, options ); - String name = !t.methodName().equals("") + String name = !t.methodName().isEmpty() ? t.methodName() //name was specified in target : stub.getSimpleName().toString(); String descriptor = t.strict() 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 mapper = new HashMap<>(); - - /** - * The public constructor. - * Should be passed a {@link Stream} of Strings, one representing each line. - * Whether they contain line endings or not is irrelevant. - * @param str a {@link Stream} of strings - */ - public ObfuscationMapper(Stream str) { - AtomicReference currentClass = new AtomicReference<>(""); - str.forEach(l -> { - if(l == 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 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 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() - )); - } - } - } -} diff --git a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java b/src/main/java/ftbsc/lll/processor/utils/ASTUtils.java similarity index 79% rename from src/main/java/ftbsc/lll/processor/tools/ASTUtils.java rename to src/main/java/ftbsc/lll/processor/utils/ASTUtils.java index 0037886..04da70e 100644 --- a/src/main/java/ftbsc/lll/processor/tools/ASTUtils.java +++ b/src/main/java/ftbsc/lll/processor/utils/ASTUtils.java @@ -1,12 +1,15 @@ -package ftbsc.lll.processor.tools; +package ftbsc.lll.processor.utils; import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.MappingNotFoundException; import ftbsc.lll.exceptions.NotAProxyException; import ftbsc.lll.exceptions.TargetNotFoundException; +import ftbsc.lll.mapper.tools.Mapper; +import ftbsc.lll.mapper.tools.data.ClassData; +import ftbsc.lll.mapper.tools.data.FieldData; +import ftbsc.lll.mapper.tools.data.MethodData; import ftbsc.lll.processor.annotations.Target; -import ftbsc.lll.processor.tools.containers.ClassContainer; -import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; +import ftbsc.lll.processor.containers.ClassContainer; import ftbsc.lll.proxies.ProxyType; import javax.annotation.processing.ProcessingEnvironment; @@ -210,35 +213,59 @@ public class ASTUtils { } /** - * 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 + * Gets the {@link ClassData} corresponding to the given fully-qualified name, + * or creates a false one with the same, non-obfuscated name twice. + * @param name the internal name of the class to convert + * @param mapper the {@link Mapper} to use, may be null * @return the fully qualified class name - * @since 0.3.0 + * @since 0.6.1 */ - public static String findClassName(String name, ProcessorOptions options) { + public static ClassData getClassData(String name, Mapper mapper) { try { - return options.mapper == null ? name : options.mapper.obfuscateClass(name).replace('/', '.'); - } catch(MappingNotFoundException e) { - return name; - } + name = name.replace('.', '/'); //just in case + if(mapper != null) + return mapper.getClassData(name); + } catch(MappingNotFoundException ignored) {} + return new ClassData(name, 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 + * Gets the {@link MethodData} corresponding to the method matching the given + * name, parent and descriptor, or creates a dummy one with fake data if no + * valid mapping is found. + * @param parent the internal name of the parent class + * @param name the name of the member + * @param descriptor the descriptor of the method + * @param mapper the {@link Mapper} to use, may be null + * @return the fully qualified class name + * @since 0.6.1 */ - public static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { + public static MethodData getMethodData(String parent, String name, String descriptor, Mapper mapper) { try { - return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); - } catch(MappingNotFoundException e) { - return memberName; - } + name = name.replace('.', '/'); //just in case + if(mapper != null) + return mapper.getMethodData(parent, name, descriptor); + } catch(MappingNotFoundException ignored) {} + return new MethodData(getClassData(name, mapper), name, name, descriptor); + } + + /** + * Gets the {@link FieldData} corresponding to the field matching the given + * name and parent, or creates a dummy one with fake data if no valid + * mapping is found. + * @param parent the internal name of the parent class + * @param name the name of the member + * @param mapper the {@link Mapper} to use, may be null + * @return the fully qualified class name + * @since 0.6.1 + */ + public static FieldData getFieldData(String parent, String name, Mapper mapper) { + try { + name = name.replace('.', '/'); //just in case + if(mapper != null) + return mapper.getFieldData(parent, name); + } catch(MappingNotFoundException ignored) {} + return new FieldData(getClassData(name, mapper), name, name); } /** @@ -258,7 +285,7 @@ public class ASTUtils { ClassContainer parent, String name, String descr, boolean strict, boolean field, ProcessingEnvironment env) { if(parent.elem == null) - throw new TargetNotFoundException("parent class", parent.fqn); + throw new TargetNotFoundException("parent class", parent.data.name); //try to find by name List candidates = parent.elem.getEnclosedElements() .stream() @@ -266,36 +293,37 @@ public class ASTUtils { .filter(e -> e.getSimpleName().contentEquals(name)) .collect(Collectors.toList()); - if(candidates.size() == 0) - throw new TargetNotFoundException(field ? "field" : "method", name, parent.fqn); + if(candidates.isEmpty()) + throw new TargetNotFoundException(field ? "field" : "method", name, parent.data.name); 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) - ); + throw new AmbiguousDefinitionException(String.format( + "Found %d members named %s in class %s!", candidates.size(), name, parent.data.name)); } 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); + throw new TargetNotFoundException("field", String.format( + "%s with descriptor %s", name, descr), parent.data.name); } 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]) + : 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.isEmpty()) + throw new TargetNotFoundException("method", String.format( + "%s %s", name, descr), parent.data.name); if(candidates.size() > 1) - throw new AmbiguousDefinitionException( - String.format("Found %d methods named %s in class %s!", candidates.size(), name, parent.fqn) - ); + throw new AmbiguousDefinitionException(String.format( + "Found %d methods named %s in class %s!", candidates.size(), name, parent.data.name)); return candidates.get(0); } } diff --git a/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java b/src/main/java/ftbsc/lll/processor/utils/JavaPoetUtils.java similarity index 90% rename from src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java rename to src/main/java/ftbsc/lll/processor/utils/JavaPoetUtils.java index 77bbc2b..629f77a 100644 --- a/src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java +++ b/src/main/java/ftbsc/lll/processor/utils/JavaPoetUtils.java @@ -1,12 +1,13 @@ -package ftbsc.lll.processor.tools; +package ftbsc.lll.processor.utils; import com.squareup.javapoet.*; +import ftbsc.lll.processor.ProcessorOptions; 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.processor.containers.ClassContainer; +import ftbsc.lll.processor.containers.FieldContainer; +import ftbsc.lll.processor.containers.InjectorInfo; +import ftbsc.lll.processor.containers.MethodContainer; import ftbsc.lll.proxies.ProxyType; import ftbsc.lll.proxies.impl.FieldProxy; import ftbsc.lll.proxies.impl.MethodProxy; @@ -15,8 +16,8 @@ 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; +import static ftbsc.lll.processor.utils.ASTUtils.getProxyType; +import static ftbsc.lll.processor.utils.ASTUtils.mapModifiers; /** * Collection of static utils that rely on JavaPoet to function. @@ -65,13 +66,13 @@ public class JavaPoetUtils { if(isMethod) { MethodContainer mc = MethodContainer.from(stub, t, f, options); descriptorObf = mc.descriptorObf; - nameObf = mc.nameObf; + nameObf = mc.data.nameMapped; parent = mc.parent; target = mc.elem; } else { FieldContainer fc = FieldContainer.from(var, options); descriptorObf = fc.descriptorObf; - nameObf = fc.nameObf; + nameObf = fc.data.nameMapped; parent = fc.parent; target = fc.elem; } @@ -88,7 +89,7 @@ public class JavaPoetUtils { con.addStatement( "$L.setParent($S, $L)", builderName, - parent.fqnObf, + parent.data.nameMapped.replace('/', '.'), parent.elem == null ? 0 : mapModifiers(parent.elem.getModifiers()) );