From 2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 17:02:46 +0200 Subject: Initial port to ASM --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 588 +++++---------------- 1 file changed, 121 insertions(+), 467 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index d0d0f2c..972d4fe 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -12,113 +12,68 @@ package cuchaz.enigma.analysis; import com.google.common.collect.*; +import cuchaz.enigma.bytecode.AccessFlags; import cuchaz.enigma.mapping.*; -import cuchaz.enigma.mapping.Translator; -import javassist.*; -import javassist.bytecode.*; -import javassist.expr.*; +import org.objectweb.asm.Opcodes; -import java.lang.reflect.Modifier; import java.util.*; -import java.util.jar.JarFile; public class JarIndex { + private final ReferencedEntryPool entryPool; + private Set obfClassEntries; private TranslationIndex translationIndex; private Map access; - private Multimap fields; - private Multimap behaviors; - private Multimap methodImplementations; - private Multimap> behaviorReferences; - private Multimap> fieldReferences; + private Multimap fields; + private Multimap methods; + private Multimap methodImplementations; + private Multimap> methodReferences; + private Multimap> fieldReferences; private Multimap innerClassesByOuter; private Map outerClassesByInner; - private Map anonymousClasses; private Map bridgedMethods; private Set syntheticMethods; - public JarIndex() { + public JarIndex(ReferencedEntryPool entryPool) { + this.entryPool = entryPool; this.obfClassEntries = Sets.newHashSet(); - this.translationIndex = new TranslationIndex(); + this.translationIndex = new TranslationIndex(entryPool); this.access = Maps.newHashMap(); this.fields = HashMultimap.create(); - this.behaviors = HashMultimap.create(); + this.methods = HashMultimap.create(); this.methodImplementations = HashMultimap.create(); - this.behaviorReferences = HashMultimap.create(); + this.methodReferences = HashMultimap.create(); this.fieldReferences = HashMultimap.create(); this.innerClassesByOuter = HashMultimap.create(); this.outerClassesByInner = Maps.newHashMap(); - this.anonymousClasses = Maps.newHashMap(); this.bridgedMethods = Maps.newHashMap(); this.syntheticMethods = Sets.newHashSet(); } - public void indexJar(JarFile jar, boolean buildInnerClasses) { + public void indexJar(ParsedJar jar, boolean buildInnerClasses) { // step 1: read the class names - this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); - - // step 2: index field/method/constructor access - for (CtClass c : JarClassIterator.classes(jar)) { - for (CtField field : c.getDeclaredFields()) { - FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); - this.access.put(fieldEntry, Access.get(field)); - this.fields.put(fieldEntry.getClassEntry(), fieldEntry); - } - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - this.access.put(behaviorEntry, Access.get(behavior)); - this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); - } - } + obfClassEntries.addAll(jar.getClassEntries()); - // step 3: index extends, implements, fields, and methods - for (CtClass c : JarClassIterator.classes(jar)) { - this.translationIndex.indexClass(c); - String className = Descriptor.toJvmName(c.getName()); - for (String interfaceName : c.getClassFile().getInterfaces()) { - className = Descriptor.toJvmName(className); - interfaceName = Descriptor.toJvmName(interfaceName); - if (className.equals(interfaceName)) { - throw new IllegalArgumentException("Class cannot be its own interface! " + className); - } - } - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - indexBehavior(behavior); - } - } + // step 2: index classes, fields, methods, interfaces + jar.visit(node -> node.accept(new IndexClassVisitor(this, Opcodes.ASM5))); - // step 4: index field, method, constructor references - for (CtClass c : JarClassIterator.classes(jar)) { - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - indexBehaviorReferences(behavior); + // step 3: index field, method, constructor references + jar.visit(node -> node.accept(new IndexReferenceVisitor(this, entryPool, Opcodes.ASM5))); + + // step 4: index bridged methods + for (MethodDefEntry methodEntry : methods.values()) { + // look for bridge and bridged methods + MethodEntry bridgedMethod = findBridgedMethod(methodEntry); + if (bridgedMethod != null) { + this.bridgedMethods.put(methodEntry, bridgedMethod); } } if (buildInnerClasses) { - // step 5: index inner classes and anonymous classes - for (CtClass c : JarClassIterator.classes(jar)) { - ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); - ClassEntry outerClassEntry = findOuterClass(c); - if (outerClassEntry != null) { - this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); - boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; - assert (innerWasAdded); - - BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); - if (enclosingBehavior != null) { - this.anonymousClasses.put(innerClassEntry, enclosingBehavior); - - // DEBUG - //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); - }/* else { - // DEBUG - //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); - }*/ - } - } + jar.visit(node -> node.accept(new IndexInnerClassVisitor(this, Opcodes.ASM5))); // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); @@ -133,385 +88,109 @@ public class JarIndex { EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); this.translationIndex.renameClasses(renames); EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); - EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); + EntryRenamer.renameClassesInMultimap(renames, this.methodReferences); EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); EntryRenamer.renameClassesInMap(renames, this.access); } } - private void indexBehavior(CtBehavior behavior) { - // get the behavior entry - final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - if (behaviorEntry instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) behaviorEntry; - - // is synthetic - if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { - syntheticMethods.add(methodEntry); - } - - // index implementation - this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); - - // look for bridge and bridged methods - CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); - if (bridgedMethod != null) { - this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); + protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) { + for (String interfaceName : interfaces) { + if (name.equals(interfaceName)) { + throw new IllegalArgumentException("Class cannot be its own interface! " + name); } } - // looks like we don't care about constructors here - } - - private void indexBehaviorReferences(CtBehavior behavior) { - // index method calls - final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - try { - behavior.instrument(new ExprEditor() { - @Override - public void edit(MethodCall call) { - MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { - calledMethodEntry = new MethodEntry( - resolvedClassEntry, - calledMethodEntry.getName(), - calledMethodEntry.getSignature() - ); - } - EntryReference reference = new EntryReference<>( - calledMethodEntry, - call.getMethodName(), - behaviorEntry - ); - behaviorReferences.put(calledMethodEntry, reference); - } - - @Override - public void edit(FieldAccess call) { - FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); - ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); - if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { - calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); - } - EntryReference reference = new EntryReference<>( - calledFieldEntry, - call.getFieldName(), - behaviorEntry - ); - fieldReferences.put(calledFieldEntry, reference); - } - - @Override - public void edit(ConstructorCall call) { - ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); - EntryReference reference = new EntryReference<>( - calledConstructorEntry, - call.getMethodName(), - behaviorEntry - ); - behaviorReferences.put(calledConstructorEntry, reference); - } - - @Override - public void edit(NewExpr call) { - ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); - EntryReference reference = new EntryReference<>( - calledConstructorEntry, - call.getClassName(), - behaviorEntry - ); - behaviorReferences.put(calledConstructorEntry, reference); - } - }); - } catch (CannotCompileException ex) { - throw new Error(ex); - } + return this.translationIndex.indexClass(access, name, superName, interfaces); } - private CtMethod getBridgedMethod(CtMethod method) { - - // bridge methods just call another method, cast it to the return type, and return the result - // let's see if we can detect this scenario - - // skip non-synthetic methods - if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { - return null; - } - - // get all the called methods - final List methodCalls = Lists.newArrayList(); - try { - method.instrument(new ExprEditor() { - @Override - public void edit(MethodCall call) { - methodCalls.add(call); - } - }); - } catch (CannotCompileException ex) { - // this is stupid... we're not even compiling anything - throw new Error(ex); - } - - // is there just one? - if (methodCalls.size() != 1) { - return null; - } - MethodCall call = methodCalls.get(0); - - try { - // we have a bridge method! - return call.getMethod(); - } catch (NotFoundException ex) { - // can't find the type? not a bridge method - return null; - } + protected void indexField(ClassDefEntry owner, int access, String name, String desc) { + FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), new AccessFlags(access)); + this.translationIndex.indexField(fieldEntry); + this.access.put(fieldEntry, Access.get(access)); + this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry); } - private ClassEntry findOuterClass(CtClass c) { - - ClassEntry classEntry = EntryFactory.getClassEntry(c); + protected void indexMethod(ClassDefEntry owner, int access, String name, String desc) { + MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), new AccessFlags(access)); + this.translationIndex.indexMethod(methodEntry); + this.access.put(methodEntry, Access.get(access)); + this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry); - // does this class already have an outer class? - if (classEntry.isInnerClass()) { - return classEntry.getOuterClassEntry(); + if (new AccessFlags(access).isSynthetic()) { + syntheticMethods.add(methodEntry); } - // inner classes: - // have constructors that can (illegally) set synthetic fields - // the outer class is the only class that calls constructors - - // use the synthetic fields to find the synthetic constructors - for (CtConstructor constructor : c.getDeclaredConstructors()) { - Set syntheticFieldTypes = Sets.newHashSet(); - if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { - continue; - } - - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); - - // gather the classes from the illegally-set synthetic fields - Set illegallySetClasses = Sets.newHashSet(); - for (String type : syntheticFieldTypes) { - if (type.startsWith("L")) { - ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); - if (isSaneOuterClass(outerClassEntry, classEntry)) { - illegallySetClasses.add(outerClassEntry); - } - } - } - - // who calls this constructor? - Set callerClasses = Sets.newHashSet(); - for (EntryReference reference : getBehaviorReferences(constructorEntry)) { - - // make sure it's not a call to super - if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { - - // is the entry a superclass of the context? - ClassEntry calledClassEntry = reference.entry.getClassEntry(); - ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); - if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { - // it's a super call, skip - continue; - } - } - - if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { - callerClasses.add(reference.context.getClassEntry()); - } - } - - // do we have an answer yet? - if (callerClasses.isEmpty()) { - if (illegallySetClasses.size() == 1) { - return illegallySetClasses.iterator().next(); - } else { - System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); - } - } else { - if (callerClasses.size() == 1) { - return callerClasses.iterator().next(); - } else { - // multiple callers, do the illegally set classes narrow it down? - Set intersection = Sets.newHashSet(callerClasses); - intersection.retainAll(illegallySetClasses); - if (intersection.size() == 1) { - return intersection.iterator().next(); - } else { - System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); - } - } - } + // we don't care about constructors here + if (!methodEntry.isConstructor()) { + // index implementation + this.methodImplementations.put(methodEntry.getClassName(), methodEntry); } - - return null; } - private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { - - // clearly this would be silly - if (outerClassEntry.equals(innerClassEntry)) { - return false; + protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) { + MethodEntry referencedMethod = new MethodEntry(entryPool.getClass(owner), name, new MethodDescriptor(desc)); + ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) { + referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry); } - - // is the outer class in the jar? - return this.obfClassEntries.contains(outerClassEntry); - + methodReferences.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); } - @SuppressWarnings("unchecked") - private boolean isIllegalConstructor(Set syntheticFieldTypes, CtConstructor constructor) { - - // illegal constructors only set synthetic member fields, then call super() - String className = constructor.getDeclaringClass().getName(); - - // collect all the field accesses, constructor calls, and method calls - final List illegalFieldWrites = Lists.newArrayList(); - final List constructorCalls = Lists.newArrayList(); - try { - constructor.instrument(new ExprEditor() { - @Override - public void edit(FieldAccess fieldAccess) { - if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { - illegalFieldWrites.add(fieldAccess); - } - } - - @Override - public void edit(ConstructorCall constructorCall) { - constructorCalls.add(constructorCall); - } - }); - } catch (CannotCompileException ex) { - // we're not compiling anything... this is stupid - throw new Error(ex); - } - - // are there any illegal field writes? - if (illegalFieldWrites.isEmpty()) { - return false; - } - - // are all the writes to synthetic fields? - for (FieldAccess fieldWrite : illegalFieldWrites) { - - // all illegal writes have to be to the local class - if (!fieldWrite.getClassName().equals(className)) { - System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); - return false; - } - - // find the field - FieldInfo fieldInfo = null; - for (FieldInfo info : (List) constructor.getDeclaringClass().getClassFile().getFields()) { - if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { - fieldInfo = info; - break; - } - } - if (fieldInfo == null) { - // field is in a superclass or something, can't be a local synthetic member - return false; - } - - // is this field synthetic? - boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if (isSynthetic) { - syntheticFieldTypes.add(fieldInfo.getDescriptor()); - } else { - System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); - return false; - } + protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) { + FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc)); + ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField); + if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) { + referencedField = referencedField.updateOwnership(resolvedClassEntry); } - - // we passed all the tests! - return true; + fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry)); } - private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { - - // is this class already marked anonymous? - EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttribute != null) { - if (enclosingMethodAttribute.methodIndex() > 0) { - return EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttribute.className()), - enclosingMethodAttribute.methodName(), - enclosingMethodAttribute.methodDescriptor() - ); - } else { - // an attribute but no method? assume not anonymous - return null; - } - } - - // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous - InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (innerClassesAttribute != null) { - return null; - } + public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) { + this.innerClassesByOuter.put(outerEntry, innerEntry); + boolean innerWasAdded = this.outerClassesByInner.put(innerEntry, outerEntry) == null; + assert (innerWasAdded); + } - ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + private MethodEntry findBridgedMethod(MethodDefEntry method) { - // anonymous classes: - // can't be abstract - // have only one constructor - // it's called exactly once by the outer class - // the type the instance is assigned to can't be this type + // bridge methods just call another method, cast it to the return desc, and return the result + // let's see if we can detect this scenario - // is abstract? - if (Modifier.isAbstract(c.getModifiers())) { + // skip non-synthetic methods + if (!method.getAccess().isSynthetic()) { return null; } - // is there exactly one constructor? - if (c.getDeclaredConstructors().length != 1) { - return null; - } - CtConstructor constructor = c.getDeclaredConstructors()[0]; + // get all the called methods + final Collection> referencedMethods = methodReferences.get(method); - // is this constructor called exactly once? - ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); - Collection> references = getBehaviorReferences(constructorEntry); - if (references.size() != 1) { + // is there just one? + if (referencedMethods.size() != 1) { return null; } - // does the caller use this type? - BehaviorEntry caller = references.iterator().next().context; - for (FieldEntry fieldEntry : getReferencedFields(caller)) { - if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { - // caller references this type, so it can't be anonymous - return null; - } - } - for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { - if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { - return null; - } - } - - return caller; + // we have a bridge method! + return referencedMethods.stream().findFirst().get().entry; } public Set getObfClassEntries() { return this.obfClassEntries; } - public Collection getObfFieldEntries() { + public Collection getObfFieldEntries() { return this.fields.values(); } - public Collection getObfFieldEntries(ClassEntry classEntry) { + public Collection getObfFieldEntries(ClassEntry classEntry) { return this.fields.get(classEntry); } - public Collection getObfBehaviorEntries() { - return this.behaviors.values(); + public Collection getObfBehaviorEntries() { + return this.methods.values(); } - public Collection getObfBehaviorEntries(ClassEntry classEntry) { - return this.behaviors.get(classEntry); + public Collection getObfBehaviorEntries(ClassEntry classEntry) { + return this.methods.get(classEntry); } public TranslationIndex getTranslationIndex() { @@ -533,8 +212,8 @@ public class JarIndex { } } ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( - deobfuscatingTranslator, - ancestry.get(ancestry.size() - 1) + deobfuscatingTranslator, + ancestry.get(ancestry.size() - 1) ); // expand all children recursively @@ -557,28 +236,20 @@ public class JarIndex { public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { // travel to the ancestor implementation - ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); - for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { - MethodEntry ancestorMethodEntry = new MethodEntry( - new ClassEntry(ancestorClassEntry), - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - if (containsObfBehavior(ancestorMethodEntry)) { + ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry(); + for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getOwnerClassEntry())) { + MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); + if (ancestorMethodEntry != null && containsObfMethod(ancestorMethodEntry)) { baseImplementationClassEntry = ancestorClassEntry; } } // make a root node at the base - MethodEntry methodEntry = new MethodEntry( - baseImplementationClassEntry, - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); + MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( - deobfuscatingTranslator, - methodEntry, - containsObfBehavior(methodEntry) + deobfuscatingTranslator, + methodEntry, + containsObfMethod(methodEntry) ); // expand the full tree @@ -599,12 +270,8 @@ public class JarIndex { for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { // is this method defined in this interface? - MethodEntry methodInterface = new MethodEntry( - interfaceEntry, - obfMethodEntry.getName(), - obfMethodEntry.getSignature() - ); - if (containsObfBehavior(methodInterface)) { + MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString()); + if (methodInterface != null && containsObfMethod(methodInterface)) { interfaceMethodEntries.add(methodInterface); } } @@ -623,14 +290,14 @@ public class JarIndex { public Set getRelatedMethodImplementations(MethodEntry obfMethodEntry) { Set methodEntries = Sets.newHashSet(); - getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); + getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry)); return methodEntries; } private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); - if (containsObfBehavior(methodEntry)) { + if (containsObfMethod(methodEntry)) { // collect the entry methodEntries.add(methodEntry); } @@ -643,7 +310,7 @@ public class JarIndex { } // look at interface methods too - for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { + for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) { getRelatedMethodImplementations(methodEntries, implementationsNode); } @@ -655,7 +322,7 @@ public class JarIndex { private void getRelatedMethodImplementations(Set methodEntries, MethodImplementationsTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); - if (containsObfBehavior(methodEntry)) { + if (containsObfMethod(methodEntry)) { // collect the entry methodEntries.add(methodEntry); } @@ -673,30 +340,30 @@ public class JarIndex { } } - public Collection> getFieldReferences(FieldEntry fieldEntry) { + public Collection> getFieldReferences(FieldEntry fieldEntry) { return this.fieldReferences.get(fieldEntry); } - public Collection getReferencedFields(BehaviorEntry behaviorEntry) { + public Collection getReferencedFields(MethodDefEntry methodEntry) { // linear search is fast enough for now Set fieldEntries = Sets.newHashSet(); - for (EntryReference reference : this.fieldReferences.values()) { - if (reference.context == behaviorEntry) { + for (EntryReference reference : this.fieldReferences.values()) { + if (reference.context == methodEntry) { fieldEntries.add(reference.entry); } } return fieldEntries; } - public Collection> getBehaviorReferences(BehaviorEntry behaviorEntry) { - return this.behaviorReferences.get(behaviorEntry); + public Collection> getMethodReferences(MethodEntry methodEntry) { + return this.methodReferences.get(methodEntry); } - public Collection getReferencedBehaviors(BehaviorEntry behaviorEntry) { + public Collection getReferencedMethods(MethodDefEntry methodEntry) { // linear search is fast enough for now - Set behaviorEntries = Sets.newHashSet(); - for (EntryReference reference : this.behaviorReferences.values()) { - if (reference.context == behaviorEntry) { + Set behaviorEntries = Sets.newHashSet(); + for (EntryReference reference : this.methodReferences.values()) { + if (reference.context == methodEntry) { behaviorEntries.add(reference.entry); } } @@ -711,20 +378,12 @@ public class JarIndex { return this.outerClassesByInner.get(obfInnerClassEntry); } - public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { - return this.anonymousClasses.containsKey(obfInnerClassEntry); - } - public boolean isSyntheticMethod(MethodEntry methodEntry) { return this.syntheticMethods.contains(methodEntry); } - public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { - return this.anonymousClasses.get(obfInnerClassName); - } - public Set getInterfaces(String className) { - ClassEntry classEntry = new ClassEntry(className); + ClassEntry classEntry = entryPool.getClass(className); Set interfaces = new HashSet<>(); interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { @@ -754,7 +413,7 @@ public class JarIndex { } public boolean isInterface(String className) { - return this.translationIndex.isInterface(new ClassEntry(className)); + return this.translationIndex.isInterface(entryPool.getClass(className)); } public boolean containsObfClass(ClassEntry obfClassEntry) { @@ -765,8 +424,8 @@ public class JarIndex { return this.access.containsKey(obfFieldEntry); } - public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { - return this.access.containsKey(obfBehaviorEntry); + public boolean containsObfMethod(MethodEntry obfMethodEntry) { + return this.access.containsKey(obfMethodEntry); } public boolean containsEntryWithSameName(Entry entry) { @@ -776,15 +435,13 @@ public class JarIndex { return false; } - public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { + public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) { // check the behavior - if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { + if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) { return false; } - // check the argument - return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); - + return true; } public boolean containsObfEntry(Entry obfEntry) { @@ -792,15 +449,12 @@ public class JarIndex { return containsObfClass((ClassEntry) obfEntry); } else if (obfEntry instanceof FieldEntry) { return containsObfField((FieldEntry) obfEntry); - } else if (obfEntry instanceof BehaviorEntry) { - return containsObfBehavior((BehaviorEntry) obfEntry); - } else if (obfEntry instanceof ArgumentEntry) { - return containsObfArgument((ArgumentEntry) obfEntry); + } else if (obfEntry instanceof MethodEntry) { + return containsObfMethod((MethodEntry) obfEntry); } else if (obfEntry instanceof LocalVariableEntry) { - // TODO: Implement it - return false; + return containsObfVariable((LocalVariableEntry) obfEntry); } else { - throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); + throw new Error("Entry desc not supported: " + obfEntry.getClass().getName()); } } -- cgit v1.2.3 From 406b9a89318473571d27de60b8aa1b51f84af245 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 17:06:26 +0200 Subject: Package updates --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 972d4fe..54dd506 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -14,6 +14,7 @@ package cuchaz.enigma.analysis; import com.google.common.collect.*; import cuchaz.enigma.bytecode.AccessFlags; import cuchaz.enigma.mapping.*; +import cuchaz.enigma.mapping.entry.*; import org.objectweb.asm.Opcodes; import java.util.*; -- cgit v1.2.3 From d72aad82c795726efcf6145f5a4172e4c8d3a5b2 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 20:38:14 +0200 Subject: Signature refactoring --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 54dd506..3479c5d 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -61,7 +61,7 @@ public class JarIndex { jar.visit(node -> node.accept(new IndexClassVisitor(this, Opcodes.ASM5))); // step 3: index field, method, constructor references - jar.visit(node -> node.accept(new IndexReferenceVisitor(this, entryPool, Opcodes.ASM5))); + jar.visit(node -> node.accept(new IndexReferenceVisitor(this, Opcodes.ASM5))); // step 4: index bridged methods for (MethodDefEntry methodEntry : methods.values()) { @@ -95,24 +95,24 @@ public class JarIndex { } } - protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) { + protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) { for (String interfaceName : interfaces) { if (name.equals(interfaceName)) { throw new IllegalArgumentException("Class cannot be its own interface! " + name); } } - return this.translationIndex.indexClass(access, name, superName, interfaces); + return this.translationIndex.indexClass(access, name, signature, superName, interfaces); } - protected void indexField(ClassDefEntry owner, int access, String name, String desc) { - FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), new AccessFlags(access)); + protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) { + FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access)); this.translationIndex.indexField(fieldEntry); this.access.put(fieldEntry, Access.get(access)); this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry); } - protected void indexMethod(ClassDefEntry owner, int access, String name, String desc) { - MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), new AccessFlags(access)); + protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) { + MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); this.translationIndex.indexMethod(methodEntry); this.access.put(methodEntry, Access.get(access)); this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry); -- cgit v1.2.3 From dbd881184fb4955b3ecd027b35b57258fd9f3480 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Thu, 21 Jun 2018 18:43:42 +0200 Subject: Fix issues with inner class signature transformation --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 3479c5d..9cecd76 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -171,7 +171,7 @@ public class JarIndex { } // we have a bridge method! - return referencedMethods.stream().findFirst().get().entry; + return referencedMethods.stream().filter(ref -> ref.entry != method).findFirst().map(ref -> ref.entry).orElse(null); } public Set getObfClassEntries() { -- cgit v1.2.3 From 561d261c921dceb9a3fe7450183862c4e0814e88 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Thu, 21 Jun 2018 19:05:40 +0200 Subject: Fix bridged method detection --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 9cecd76..2f3501d 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -171,7 +171,7 @@ public class JarIndex { } // we have a bridge method! - return referencedMethods.stream().filter(ref -> ref.entry != method).findFirst().map(ref -> ref.entry).orElse(null); + return referencedMethods.stream().map(ref -> ref.context).filter(Objects::nonNull).findFirst().orElse(null); } public Set getObfClassEntries() { -- cgit v1.2.3 From 21cc365c2c24c586b526f91806c8c27c3cddebe5 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Fri, 22 Jun 2018 22:00:48 +0200 Subject: Fix compile issues --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 2f3501d..87e6e88 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -385,8 +385,7 @@ public class JarIndex { public Set getInterfaces(String className) { ClassEntry classEntry = entryPool.getClass(className); - Set interfaces = new HashSet<>(); - interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); + Set interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry)); for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); } -- cgit v1.2.3 From 7ec433f1ecc5b44f8b690c7443d5e38ac0e6422d Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Fri, 22 Jun 2018 22:29:41 +0200 Subject: Resolve all failed tests --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 87e6e88..8172dea 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -148,8 +148,7 @@ public class JarIndex { public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) { this.innerClassesByOuter.put(outerEntry, innerEntry); - boolean innerWasAdded = this.outerClassesByInner.put(innerEntry, outerEntry) == null; - assert (innerWasAdded); + this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry); } private MethodEntry findBridgedMethod(MethodDefEntry method) { -- cgit v1.2.3 From 8a0e350a04e570074557ff0a53d67e82d54d3005 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sun, 24 Jun 2018 12:24:48 +0200 Subject: Fix method reference and bridge detection --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 102 ++++++++++++++------- 1 file changed, 68 insertions(+), 34 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 8172dea..27a8e07 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -29,11 +29,13 @@ public class JarIndex { private Multimap fields; private Multimap methods; private Multimap methodImplementations; - private Multimap> methodReferences; + private Multimap> methodsReferencing; + private Multimap methodReferences; private Multimap> fieldReferences; private Multimap innerClassesByOuter; private Map outerClassesByInner; private Map bridgedMethods; + private Map accessMethods; private Set syntheticMethods; public JarIndex(ReferencedEntryPool entryPool) { @@ -44,11 +46,13 @@ public class JarIndex { this.fields = HashMultimap.create(); this.methods = HashMultimap.create(); this.methodImplementations = HashMultimap.create(); + this.methodsReferencing = HashMultimap.create(); this.methodReferences = HashMultimap.create(); this.fieldReferences = HashMultimap.create(); this.innerClassesByOuter = HashMultimap.create(); this.outerClassesByInner = Maps.newHashMap(); this.bridgedMethods = Maps.newHashMap(); + this.accessMethods = Maps.newHashMap(); this.syntheticMethods = Sets.newHashSet(); } @@ -63,12 +67,15 @@ public class JarIndex { // step 3: index field, method, constructor references jar.visit(node -> node.accept(new IndexReferenceVisitor(this, Opcodes.ASM5))); - // step 4: index bridged methods + // step 4: index access and bridged methods for (MethodDefEntry methodEntry : methods.values()) { - // look for bridge and bridged methods - MethodEntry bridgedMethod = findBridgedMethod(methodEntry); - if (bridgedMethod != null) { - this.bridgedMethods.put(methodEntry, bridgedMethod); + // look for access and bridged methods + MethodEntry accessedMethod = findAccessMethod(methodEntry); + if (accessedMethod != null) { + this.accessMethods.put(methodEntry, accessedMethod); + if (isBridgedMethod(accessedMethod, methodEntry)) { + this.bridgedMethods.put(methodEntry, accessedMethod); + } } } @@ -89,6 +96,7 @@ public class JarIndex { EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); this.translationIndex.renameClasses(renames); EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); + EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing); EntryRenamer.renameClassesInMultimap(renames, this.methodReferences); EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); EntryRenamer.renameClassesInMap(renames, this.access); @@ -134,7 +142,8 @@ public class JarIndex { if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) { referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry); } - methodReferences.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); + methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry)); + methodReferences.put(callerEntry, referencedMethod); } protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) { @@ -151,26 +160,54 @@ public class JarIndex { this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry); } - private MethodEntry findBridgedMethod(MethodDefEntry method) { + private MethodEntry findAccessMethod(MethodDefEntry method) { - // bridge methods just call another method, cast it to the return desc, and return the result - // let's see if we can detect this scenario + // we want to find all compiler-added methods that directly call another with no processing // skip non-synthetic methods if (!method.getAccess().isSynthetic()) { return null; } - // get all the called methods - final Collection> referencedMethods = methodReferences.get(method); + // get all the methods that we call + final Collection referencedMethods = methodReferences.get(method); // is there just one? if (referencedMethods.size() != 1) { return null; } - // we have a bridge method! - return referencedMethods.stream().map(ref -> ref.context).filter(Objects::nonNull).findFirst().orElse(null); + return referencedMethods.stream().findFirst().orElse(null); + } + + private boolean isBridgedMethod(MethodEntry called, MethodEntry access) { + // Bridged methods will always have the same name as the method they are calling + // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed) + if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) { + return false; + } + + TypeDescriptor accessReturn = access.getDesc().getReturnDesc(); + TypeDescriptor calledReturn = called.getDesc().getReturnDesc(); + if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) { + return false; + } + + // Bridged methods will never have the same type as what they are calling + if (accessReturn.equals(calledReturn)) { + return false; + } + + String accessType = accessReturn.toString(); + + // If we're casting down from generic type to type-erased Object we're a bridge method + if (accessType.equals("Ljava/lang/Object;")) { + return true; + } + + // Now we need to detect cases where we are being casted down to a higher type bound + List calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry()); + return calledAncestry.contains(accessReturn.getTypeEntry()); } public Set getObfClassEntries() { @@ -302,11 +339,11 @@ public class JarIndex { methodEntries.add(methodEntry); } - // look at bridged methods! - MethodEntry bridgedEntry = getBridgedMethod(methodEntry); - while (bridgedEntry != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); - bridgedEntry = getBridgedMethod(bridgedEntry); + // look at access methods! + MethodEntry accessMethod = getAccessMethod(methodEntry); + while (accessMethod != null) { + methodEntries.addAll(getRelatedMethodImplementations(accessMethod)); + accessMethod = getAccessMethod(accessMethod); } // look at interface methods too @@ -327,11 +364,11 @@ public class JarIndex { methodEntries.add(methodEntry); } - // look at bridged methods! - MethodEntry bridgedEntry = getBridgedMethod(methodEntry); - while (bridgedEntry != null) { - methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); - bridgedEntry = getBridgedMethod(bridgedEntry); + // look at access methods! + MethodEntry accessMethod = getAccessMethod(methodEntry); + while (accessMethod != null) { + methodEntries.addAll(getRelatedMethodImplementations(accessMethod)); + accessMethod = getAccessMethod(accessMethod); } // recurse @@ -355,19 +392,12 @@ public class JarIndex { return fieldEntries; } - public Collection> getMethodReferences(MethodEntry methodEntry) { - return this.methodReferences.get(methodEntry); + public Collection> getMethodsReferencing(MethodEntry methodEntry) { + return this.methodsReferencing.get(methodEntry); } public Collection getReferencedMethods(MethodDefEntry methodEntry) { - // linear search is fast enough for now - Set behaviorEntries = Sets.newHashSet(); - for (EntryReference reference : this.methodReferences.values()) { - if (reference.context == methodEntry) { - behaviorEntries.add(reference.entry); - } - } - return behaviorEntries; + return this.methodReferences.get(methodEntry); } public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { @@ -461,6 +491,10 @@ public class JarIndex { return this.bridgedMethods.get(bridgeMethodEntry); } + public MethodEntry getAccessMethod(MethodEntry bridgeMethodEntry) { + return this.accessMethods.get(bridgeMethodEntry); + } + public List getObfClassChain(ClassEntry obfClassEntry) { // build class chain in inner-to-outer order -- cgit v1.2.3 From 319c9aeed8d9d1b225eb3234b439f899d0054ec4 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Mon, 25 Jun 2018 09:39:39 +0200 Subject: Fix stackoverflow on rebuilding method names --- src/main/java/cuchaz/enigma/analysis/JarIndex.java | 30 ++++++++++------------ 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java') diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index 27a8e07..5917a32 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java @@ -35,7 +35,6 @@ public class JarIndex { private Multimap innerClassesByOuter; private Map outerClassesByInner; private Map bridgedMethods; - private Map accessMethods; private Set syntheticMethods; public JarIndex(ReferencedEntryPool entryPool) { @@ -52,7 +51,6 @@ public class JarIndex { this.innerClassesByOuter = HashMultimap.create(); this.outerClassesByInner = Maps.newHashMap(); this.bridgedMethods = Maps.newHashMap(); - this.accessMethods = Maps.newHashMap(); this.syntheticMethods = Sets.newHashSet(); } @@ -72,7 +70,6 @@ public class JarIndex { // look for access and bridged methods MethodEntry accessedMethod = findAccessMethod(methodEntry); if (accessedMethod != null) { - this.accessMethods.put(methodEntry, accessedMethod); if (isBridgedMethod(accessedMethod, methodEntry)) { this.bridgedMethods.put(methodEntry, accessedMethod); } @@ -333,17 +330,20 @@ public class JarIndex { private void getRelatedMethodImplementations(Set methodEntries, MethodInheritanceTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); + if (methodEntries.contains(methodEntry)) { + return; + } if (containsObfMethod(methodEntry)) { // collect the entry methodEntries.add(methodEntry); } - // look at access methods! - MethodEntry accessMethod = getAccessMethod(methodEntry); - while (accessMethod != null) { - methodEntries.addAll(getRelatedMethodImplementations(accessMethod)); - accessMethod = getAccessMethod(accessMethod); + // look at bridge methods! + MethodEntry bridgedMethod = getBridgedMethod(methodEntry); + while (bridgedMethod != null) { + methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); + bridgedMethod = getBridgedMethod(bridgedMethod); } // look at interface methods too @@ -364,11 +364,11 @@ public class JarIndex { methodEntries.add(methodEntry); } - // look at access methods! - MethodEntry accessMethod = getAccessMethod(methodEntry); - while (accessMethod != null) { - methodEntries.addAll(getRelatedMethodImplementations(accessMethod)); - accessMethod = getAccessMethod(accessMethod); + // look at bridge methods! + MethodEntry bridgedMethod = getBridgedMethod(methodEntry); + while (bridgedMethod != null) { + methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod)); + bridgedMethod = getBridgedMethod(bridgedMethod); } // recurse @@ -491,10 +491,6 @@ public class JarIndex { return this.bridgedMethods.get(bridgeMethodEntry); } - public MethodEntry getAccessMethod(MethodEntry bridgeMethodEntry) { - return this.accessMethods.get(bridgeMethodEntry); - } - public List getObfClassChain(ClassEntry obfClassEntry) { // build class chain in inner-to-outer order -- cgit v1.2.3