diff options
| author | 2015-03-30 23:56:21 -0400 | |
|---|---|---|
| committer | 2015-03-30 23:56:21 -0400 | |
| commit | 2fc8ae770442ec3ab91cf0c16cc30917e0d048d3 (patch) | |
| tree | a49e9ede649fb0f350fee1b8336f319df20f4973 | |
| parent | add publifier (diff) | |
| download | enigma-2fc8ae770442ec3ab91cf0c16cc30917e0d048d3.tar.gz enigma-2fc8ae770442ec3ab91cf0c16cc30917e0d048d3.tar.xz enigma-2fc8ae770442ec3ab91cf0c16cc30917e0d048d3.zip | |
resolve methods using interfaces as well as superclasses
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 36 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/TranslationIndex.java | 69 |
2 files changed, 84 insertions, 21 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e255468a..6b3cf677 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -59,7 +59,6 @@ public class JarIndex { | |||
| 59 | 59 | ||
| 60 | private Set<ClassEntry> m_obfClassEntries; | 60 | private Set<ClassEntry> m_obfClassEntries; |
| 61 | private TranslationIndex m_translationIndex; | 61 | private TranslationIndex m_translationIndex; |
| 62 | private Multimap<String,String> m_interfaces; | ||
| 63 | private Map<Entry,Access> m_access; | 62 | private Map<Entry,Access> m_access; |
| 64 | private Multimap<ClassEntry,FieldEntry> m_fields; | 63 | private Multimap<ClassEntry,FieldEntry> m_fields; |
| 65 | private Multimap<ClassEntry,BehaviorEntry> m_behaviors; | 64 | private Multimap<ClassEntry,BehaviorEntry> m_behaviors; |
| @@ -74,7 +73,6 @@ public class JarIndex { | |||
| 74 | public JarIndex() { | 73 | public JarIndex() { |
| 75 | m_obfClassEntries = Sets.newHashSet(); | 74 | m_obfClassEntries = Sets.newHashSet(); |
| 76 | m_translationIndex = new TranslationIndex(); | 75 | m_translationIndex = new TranslationIndex(); |
| 77 | m_interfaces = HashMultimap.create(); | ||
| 78 | m_access = Maps.newHashMap(); | 76 | m_access = Maps.newHashMap(); |
| 79 | m_fields = HashMultimap.create(); | 77 | m_fields = HashMultimap.create(); |
| 80 | m_behaviors = HashMultimap.create(); | 78 | m_behaviors = HashMultimap.create(); |
| @@ -124,7 +122,6 @@ public class JarIndex { | |||
| 124 | if (className.equals(interfaceName)) { | 122 | if (className.equals(interfaceName)) { |
| 125 | throw new IllegalArgumentException("Class cannot be its own interface! " + className); | 123 | throw new IllegalArgumentException("Class cannot be its own interface! " + className); |
| 126 | } | 124 | } |
| 127 | m_interfaces.put(className, interfaceName); | ||
| 128 | } | 125 | } |
| 129 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 126 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 130 | indexBehavior(behavior); | 127 | indexBehavior(behavior); |
| @@ -176,7 +173,6 @@ public class JarIndex { | |||
| 176 | } | 173 | } |
| 177 | EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); | 174 | EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); |
| 178 | m_translationIndex.renameClasses(renames); | 175 | m_translationIndex.renameClasses(renames); |
| 179 | EntryRenamer.renameClassesInMultimap(renames, m_interfaces); | ||
| 180 | EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); | 176 | EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations); |
| 181 | EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); | 177 | EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences); |
| 182 | EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); | 178 | EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences); |
| @@ -637,11 +633,11 @@ public class JarIndex { | |||
| 637 | interfaceMethodEntries.add(obfMethodEntry); | 633 | interfaceMethodEntries.add(obfMethodEntry); |
| 638 | } else { | 634 | } else { |
| 639 | // get the interface class | 635 | // get the interface class |
| 640 | for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) { | 636 | for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { |
| 641 | 637 | ||
| 642 | // is this method defined in this interface? | 638 | // is this method defined in this interface? |
| 643 | MethodEntry methodInterface = new MethodEntry( | 639 | MethodEntry methodInterface = new MethodEntry( |
| 644 | new ClassEntry(interfaceName), | 640 | interfaceEntry, |
| 645 | obfMethodEntry.getName(), | 641 | obfMethodEntry.getName(), |
| 646 | obfMethodEntry.getSignature() | 642 | obfMethodEntry.getSignature() |
| 647 | ); | 643 | ); |
| @@ -745,31 +741,33 @@ public class JarIndex { | |||
| 745 | return m_anonymousClasses.get(obfInnerClassName); | 741 | return m_anonymousClasses.get(obfInnerClassName); |
| 746 | } | 742 | } |
| 747 | 743 | ||
| 748 | public Set<String> getInterfaces(String className) { | 744 | public Set<ClassEntry> getInterfaces(String className) { |
| 749 | Set<String> interfaceNames = new HashSet<String>(); | 745 | ClassEntry classEntry = new ClassEntry(className); |
| 750 | interfaceNames.addAll(m_interfaces.get(className)); | 746 | Set<ClassEntry> interfaces = new HashSet<ClassEntry>(); |
| 751 | for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) { | 747 | interfaces.addAll(m_translationIndex.getInterfaces(classEntry)); |
| 752 | interfaceNames.addAll(m_interfaces.get(ancestor.getName())); | 748 | for (ClassEntry ancestor : m_translationIndex.getAncestry(classEntry)) { |
| 749 | interfaces.addAll(m_translationIndex.getInterfaces(ancestor)); | ||
| 753 | } | 750 | } |
| 754 | return interfaceNames; | 751 | return interfaces; |
| 755 | } | 752 | } |
| 756 | 753 | ||
| 757 | public Set<String> getImplementingClasses(String targetInterfaceName) { | 754 | public Set<String> getImplementingClasses(String targetInterfaceName) { |
| 755 | |||
| 758 | // linear search is fast enough for now | 756 | // linear search is fast enough for now |
| 759 | Set<String> classNames = Sets.newHashSet(); | 757 | Set<String> classNames = Sets.newHashSet(); |
| 760 | for (Map.Entry<String,String> entry : m_interfaces.entries()) { | 758 | for (Map.Entry<ClassEntry,ClassEntry> entry : m_translationIndex.getClassInterfaces()) { |
| 761 | String className = entry.getKey(); | 759 | ClassEntry classEntry = entry.getKey(); |
| 762 | String interfaceName = entry.getValue(); | 760 | ClassEntry interfaceEntry = entry.getValue(); |
| 763 | if (interfaceName.equals(targetInterfaceName)) { | 761 | if (interfaceEntry.getName().equals(targetInterfaceName)) { |
| 764 | classNames.add(className); | 762 | classNames.add(classEntry.getClassName()); |
| 765 | m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className)); | 763 | m_translationIndex.getSubclassNamesRecursively(classNames, classEntry); |
| 766 | } | 764 | } |
| 767 | } | 765 | } |
| 768 | return classNames; | 766 | return classNames; |
| 769 | } | 767 | } |
| 770 | 768 | ||
| 771 | public boolean isInterface(String className) { | 769 | public boolean isInterface(String className) { |
| 772 | return m_interfaces.containsValue(className); | 770 | return m_translationIndex.isInterface(new ClassEntry(className)); |
| 773 | } | 771 | } |
| 774 | 772 | ||
| 775 | public boolean containsObfClass(ClassEntry obfClassEntry) { | 773 | public boolean containsObfClass(ClassEntry obfClassEntry) { |
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java index bd77344c..e0e66bf7 100644 --- a/src/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java | |||
| @@ -16,6 +16,7 @@ import java.io.ObjectInputStream; | |||
| 16 | import java.io.ObjectOutputStream; | 16 | import java.io.ObjectOutputStream; |
| 17 | import java.io.OutputStream; | 17 | import java.io.OutputStream; |
| 18 | import java.io.Serializable; | 18 | import java.io.Serializable; |
| 19 | import java.util.Collection; | ||
| 19 | import java.util.HashMap; | 20 | import java.util.HashMap; |
| 20 | import java.util.List; | 21 | import java.util.List; |
| 21 | import java.util.Map; | 22 | import java.util.Map; |
| @@ -26,6 +27,7 @@ import java.util.zip.GZIPOutputStream; | |||
| 26 | import javassist.CtBehavior; | 27 | import javassist.CtBehavior; |
| 27 | import javassist.CtClass; | 28 | import javassist.CtClass; |
| 28 | import javassist.CtField; | 29 | import javassist.CtField; |
| 30 | import javassist.bytecode.Descriptor; | ||
| 29 | 31 | ||
| 30 | import com.google.common.collect.HashMultimap; | 32 | import com.google.common.collect.HashMultimap; |
| 31 | import com.google.common.collect.Lists; | 33 | import com.google.common.collect.Lists; |
| @@ -36,8 +38,8 @@ import cuchaz.enigma.mapping.ArgumentEntry; | |||
| 36 | import cuchaz.enigma.mapping.BehaviorEntry; | 38 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 37 | import cuchaz.enigma.mapping.ClassEntry; | 39 | import cuchaz.enigma.mapping.ClassEntry; |
| 38 | import cuchaz.enigma.mapping.Entry; | 40 | import cuchaz.enigma.mapping.Entry; |
| 39 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 40 | import cuchaz.enigma.mapping.EntryFactory; | 41 | import cuchaz.enigma.mapping.EntryFactory; |
| 42 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 41 | import cuchaz.enigma.mapping.Translator; | 43 | import cuchaz.enigma.mapping.Translator; |
| 42 | 44 | ||
| 43 | public class TranslationIndex implements Serializable { | 45 | public class TranslationIndex implements Serializable { |
| @@ -47,11 +49,13 @@ public class TranslationIndex implements Serializable { | |||
| 47 | private Map<ClassEntry,ClassEntry> m_superclasses; | 49 | private Map<ClassEntry,ClassEntry> m_superclasses; |
| 48 | private Multimap<ClassEntry,FieldEntry> m_fieldEntries; | 50 | private Multimap<ClassEntry,FieldEntry> m_fieldEntries; |
| 49 | private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries; | 51 | private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries; |
| 52 | private Multimap<ClassEntry,ClassEntry> m_interfaces; | ||
| 50 | 53 | ||
| 51 | public TranslationIndex() { | 54 | public TranslationIndex() { |
| 52 | m_superclasses = Maps.newHashMap(); | 55 | m_superclasses = Maps.newHashMap(); |
| 53 | m_fieldEntries = HashMultimap.create(); | 56 | m_fieldEntries = HashMultimap.create(); |
| 54 | m_behaviorEntries = HashMultimap.create(); | 57 | m_behaviorEntries = HashMultimap.create(); |
| 58 | m_interfaces = HashMultimap.create(); | ||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | public TranslationIndex(TranslationIndex other, Translator translator) { | 61 | public TranslationIndex(TranslationIndex other, Translator translator) { |
| @@ -65,6 +69,15 @@ public class TranslationIndex implements Serializable { | |||
| 65 | ); | 69 | ); |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 72 | // translate the interfaces | ||
| 73 | m_interfaces = HashMultimap.create(); | ||
| 74 | for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_interfaces.entries()) { | ||
| 75 | m_interfaces.put( | ||
| 76 | translator.translateEntry(mapEntry.getKey()), | ||
| 77 | translator.translateEntry(mapEntry.getValue()) | ||
| 78 | ); | ||
| 79 | } | ||
| 80 | |||
| 68 | // translate the fields | 81 | // translate the fields |
| 69 | m_fieldEntries = HashMultimap.create(); | 82 | m_fieldEntries = HashMultimap.create(); |
| 70 | for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) { | 83 | for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) { |
| @@ -90,13 +103,24 @@ public class TranslationIndex implements Serializable { | |||
| 90 | public void indexClass(CtClass c, boolean indexMembers) { | 103 | public void indexClass(CtClass c, boolean indexMembers) { |
| 91 | 104 | ||
| 92 | ClassEntry classEntry = EntryFactory.getClassEntry(c); | 105 | ClassEntry classEntry = EntryFactory.getClassEntry(c); |
| 106 | if (isJre(classEntry)) { | ||
| 107 | return; | ||
| 108 | } | ||
| 93 | 109 | ||
| 94 | // add the superclass | 110 | // add the superclass |
| 95 | ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); | 111 | ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); |
| 96 | if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) { | 112 | if (superclassEntry != null && !isJre(superclassEntry)) { |
| 97 | m_superclasses.put(classEntry, superclassEntry); | 113 | m_superclasses.put(classEntry, superclassEntry); |
| 98 | } | 114 | } |
| 99 | 115 | ||
| 116 | // add the interfaces | ||
| 117 | for (String interfaceClassName : c.getClassFile().getInterfaces()) { | ||
| 118 | ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); | ||
| 119 | if (!isJre(interfaceClassEntry)) { | ||
| 120 | m_interfaces.put(classEntry, interfaceClassEntry); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 100 | if (indexMembers) { | 124 | if (indexMembers) { |
| 101 | // add fields | 125 | // add fields |
| 102 | for (CtField field : c.getDeclaredFields()) { | 126 | for (CtField field : c.getDeclaredFields()) { |
| @@ -134,6 +158,7 @@ public class TranslationIndex implements Serializable { | |||
| 134 | } | 158 | } |
| 135 | 159 | ||
| 136 | public List<ClassEntry> getSubclass(ClassEntry classEntry) { | 160 | public List<ClassEntry> getSubclass(ClassEntry classEntry) { |
| 161 | |||
| 137 | // linear search is fast enough for now | 162 | // linear search is fast enough for now |
| 138 | List<ClassEntry> subclasses = Lists.newArrayList(); | 163 | List<ClassEntry> subclasses = Lists.newArrayList(); |
| 139 | for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) { | 164 | for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) { |
| @@ -160,6 +185,18 @@ public class TranslationIndex implements Serializable { | |||
| 160 | } | 185 | } |
| 161 | } | 186 | } |
| 162 | 187 | ||
| 188 | public Collection<Map.Entry<ClassEntry,ClassEntry>> getClassInterfaces() { | ||
| 189 | return m_interfaces.entries(); | ||
| 190 | } | ||
| 191 | |||
| 192 | public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { | ||
| 193 | return m_interfaces.get(classEntry); | ||
| 194 | } | ||
| 195 | |||
| 196 | public boolean isInterface(ClassEntry classEntry) { | ||
| 197 | return m_interfaces.containsValue(classEntry); | ||
| 198 | } | ||
| 199 | |||
| 163 | public boolean entryExists(Entry entry) { | 200 | public boolean entryExists(Entry entry) { |
| 164 | if (entry instanceof FieldEntry) { | 201 | if (entry instanceof FieldEntry) { |
| 165 | return fieldExists((FieldEntry)entry); | 202 | return fieldExists((FieldEntry)entry); |
| @@ -185,6 +222,21 @@ public class TranslationIndex implements Serializable { | |||
| 185 | return (ClassEntry)entry; | 222 | return (ClassEntry)entry; |
| 186 | } | 223 | } |
| 187 | 224 | ||
| 225 | ClassEntry superclassEntry = resolveSuperclass(entry); | ||
| 226 | if (superclassEntry != null) { | ||
| 227 | return superclassEntry; | ||
| 228 | } | ||
| 229 | |||
| 230 | ClassEntry interfaceEntry = resolveInterface(entry); | ||
| 231 | if (interfaceEntry != null) { | ||
| 232 | return interfaceEntry; | ||
| 233 | } | ||
| 234 | |||
| 235 | return null; | ||
| 236 | } | ||
| 237 | |||
| 238 | public ClassEntry resolveSuperclass(Entry entry) { | ||
| 239 | |||
| 188 | // this entry could refer to a method on a class where the method is not actually implemented | 240 | // this entry could refer to a method on a class where the method is not actually implemented |
| 189 | // travel up the inheritance tree to find the closest implementation | 241 | // travel up the inheritance tree to find the closest implementation |
| 190 | while (!entryExists(entry)) { | 242 | while (!entryExists(entry)) { |
| @@ -203,6 +255,19 @@ public class TranslationIndex implements Serializable { | |||
| 203 | return entry.getClassEntry(); | 255 | return entry.getClassEntry(); |
| 204 | } | 256 | } |
| 205 | 257 | ||
| 258 | public ClassEntry resolveInterface(Entry entry) { | ||
| 259 | |||
| 260 | // the interfaces for any class is a forest | ||
| 261 | // so let's look at all the trees | ||
| 262 | for (ClassEntry interfaceEntry : m_interfaces.get(entry.getClassEntry())) { | ||
| 263 | ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); | ||
| 264 | if (resolvedClassEntry != null) { | ||
| 265 | return resolvedClassEntry; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | return null; | ||
| 269 | } | ||
| 270 | |||
| 206 | private boolean isJre(ClassEntry classEntry) { | 271 | private boolean isJre(ClassEntry classEntry) { |
| 207 | String packageName = classEntry.getPackageName(); | 272 | String packageName = classEntry.getPackageName(); |
| 208 | return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); | 273 | return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); |