summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma
diff options
context:
space:
mode:
authorGravatar jeff2015-01-19 22:22:57 -0500
committerGravatar jeff2015-01-19 22:22:57 -0500
commit2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c (patch)
tree0e9c23d6838d3e0299831dbc24b6d736c268cd8b /src/cuchaz/enigma
parentadded inverse operation for moving classes out of the default package (diff)
downloadenigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.gz
enigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.xz
enigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.zip
solved tricky issue with incorrect translation of fields/methods referenced by a subclass instead of the declaring class
Diffstat (limited to 'src/cuchaz/enigma')
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java8
-rw-r--r--src/cuchaz/enigma/Main.java2
-rw-r--r--src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java65
-rw-r--r--src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java172
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java9
-rw-r--r--src/cuchaz/enigma/mapping/JavassistUtil.java55
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java11
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java6
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java57
11 files changed, 271 insertions, 122 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 679518a..2305722 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -144,7 +144,7 @@ public class Deobfuscator {
144 // fields 144 // fields
145 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { 145 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
146 FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); 146 FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName());
147 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(fieldEntry); 147 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry);
148 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { 148 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) {
149 boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); 149 boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry);
150 if (wasMoved) { 150 if (wasMoved) {
@@ -167,7 +167,7 @@ public class Deobfuscator {
167 methodMapping.getObfName(), 167 methodMapping.getObfName(),
168 methodMapping.getObfSignature() 168 methodMapping.getObfSignature()
169 ); 169 );
170 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(methodEntry); 170 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry);
171 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { 171 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) {
172 boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); 172 boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry);
173 if (wasMoved) { 173 if (wasMoved) {
@@ -233,7 +233,7 @@ public class Deobfuscator {
233 public Translator getTranslator(TranslationDirection direction) { 233 public Translator getTranslator(TranslationDirection direction) {
234 Translator translator = m_translatorCache.get(direction); 234 Translator translator = m_translatorCache.get(direction);
235 if (translator == null) { 235 if (translator == null) {
236 translator = m_mappings.getTranslator(direction); 236 translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex());
237 m_translatorCache.put(direction, translator); 237 m_translatorCache.put(direction, translator);
238 } 238 }
239 return translator; 239 return translator;
@@ -311,7 +311,7 @@ public class Deobfuscator {
311 Entry obfEntry = obfuscateEntry(deobfReference.entry); 311 Entry obfEntry = obfuscateEntry(deobfReference.entry);
312 312
313 // try to resolve the class 313 // try to resolve the class
314 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(obfEntry); 314 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
315 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 315 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
316 // change the class of the entry 316 // change the class of the entry
317 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 317 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java
index f8d3afe..1891ded 100644
--- a/src/cuchaz/enigma/Main.java
+++ b/src/cuchaz/enigma/Main.java
@@ -28,7 +28,7 @@ public class Main {
28 } 28 }
29 29
30 // DEBUG 30 // DEBUG
31 // gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); 31 //gui.getController().openDeclaration(new ClassEntry("none/bxq"));
32 } 32 }
33 33
34 private static File getFile(String path) { 34 private static File getFile(String path) {
diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index b132305..3eaa391 100644
--- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -51,8 +51,8 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
51 public void load(TranslationIndex ancestries, boolean recurse) { 51 public void load(TranslationIndex ancestries, boolean recurse) {
52 // get all the child nodes 52 // get all the child nodes
53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
54 for (String subclassName : ancestries.getSubclassNames(m_obfClassName)) { 54 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) {
55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassName)); 55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName()));
56 } 56 }
57 57
58 // add them to this node 58 // add them to this node
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 4b03a33..c96d3bc 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -114,8 +114,8 @@ public class JarIndex {
114 // step 3: index extends, implements, fields, and methods 114 // step 3: index extends, implements, fields, and methods
115 for (CtClass c : JarClassIterator.classes(jar)) { 115 for (CtClass c : JarClassIterator.classes(jar)) {
116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); 116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
117 m_translationIndex.indexClass(c);
117 String className = Descriptor.toJvmName(c.getName()); 118 String className = Descriptor.toJvmName(c.getName());
118 m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass()));
119 for (String interfaceName : c.getClassFile().getInterfaces()) { 119 for (String interfaceName : c.getClassFile().getInterfaces()) {
120 className = Descriptor.toJvmName(className); 120 className = Descriptor.toJvmName(className);
121 interfaceName = Descriptor.toJvmName(interfaceName); 121 interfaceName = Descriptor.toJvmName(interfaceName);
@@ -191,8 +191,6 @@ public class JarIndex {
191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); 191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); 192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
193 193
194 m_translationIndex.addField(className, field.getName());
195
196 // is the field a class type? 194 // is the field a class type?
197 if (field.getSignature().startsWith("L")) { 195 if (field.getSignature().startsWith("L")) {
198 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); 196 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
@@ -230,13 +228,12 @@ public class JarIndex {
230 behavior.instrument(new ExprEditor() { 228 behavior.instrument(new ExprEditor() {
231 @Override 229 @Override
232 public void edit(MethodCall call) { 230 public void edit(MethodCall call) {
233 String className = Descriptor.toJvmName(call.getClassName());
234 MethodEntry calledMethodEntry = new MethodEntry( 231 MethodEntry calledMethodEntry = new MethodEntry(
235 new ClassEntry(className), 232 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
236 call.getMethodName(), 233 call.getMethodName(),
237 call.getSignature() 234 call.getSignature()
238 ); 235 );
239 ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry); 236 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry);
240 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { 237 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
241 calledMethodEntry = new MethodEntry( 238 calledMethodEntry = new MethodEntry(
242 resolvedClassEntry, 239 resolvedClassEntry,
@@ -254,12 +251,11 @@ public class JarIndex {
254 251
255 @Override 252 @Override
256 public void edit(FieldAccess call) { 253 public void edit(FieldAccess call) {
257 String className = Descriptor.toJvmName(call.getClassName());
258 FieldEntry calledFieldEntry = new FieldEntry( 254 FieldEntry calledFieldEntry = new FieldEntry(
259 new ClassEntry(className), 255 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
260 call.getFieldName() 256 call.getFieldName()
261 ); 257 );
262 ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry); 258 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry);
263 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { 259 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
264 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); 260 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
265 } 261 }
@@ -273,9 +269,8 @@ public class JarIndex {
273 269
274 @Override 270 @Override
275 public void edit(ConstructorCall call) { 271 public void edit(ConstructorCall call) {
276 String className = Descriptor.toJvmName(call.getClassName());
277 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 272 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
278 new ClassEntry(className), 273 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
279 call.getSignature() 274 call.getSignature()
280 ); 275 );
281 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 276 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -288,9 +283,8 @@ public class JarIndex {
288 283
289 @Override 284 @Override
290 public void edit(NewExpr call) { 285 public void edit(NewExpr call) {
291 String className = Descriptor.toJvmName(call.getClassName());
292 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 286 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
293 new ClassEntry(className), 287 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
294 call.getSignature() 288 call.getSignature()
295 ); 289 );
296 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 290 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -306,25 +300,6 @@ public class JarIndex {
306 } 300 }
307 } 301 }
308 302
309 public ClassEntry resolveEntryClass(Entry obfEntry) {
310
311 // this entry could refer to a method on a class where the method is not actually implemented
312 // travel up the inheritance tree to find the closest implementation
313 while (!containsObfEntry(obfEntry)) {
314 // is there a parent class?
315 String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName());
316 if (superclassName == null) {
317 // this is probably a method from a class in a library
318 // we can't trace the implementation up any higher unless we index the library
319 return null;
320 }
321
322 // move up to the parent class
323 obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName));
324 }
325 return obfEntry.getClassEntry();
326 }
327
328 private CtMethod getBridgedMethod(CtMethod method) { 303 private CtMethod getBridgedMethod(CtMethod method) {
329 304
330 // bridge methods just call another method, cast it to the return type, and return the result 305 // bridge methods just call another method, cast it to the return type, and return the result
@@ -402,9 +377,9 @@ public class JarIndex {
402 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { 377 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
403 378
404 // is the entry a superclass of the context? 379 // is the entry a superclass of the context?
405 String calledClassName = reference.entry.getClassName(); 380 ClassEntry calledClassEntry = reference.entry.getClassEntry();
406 String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName()); 381 ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry());
407 if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) { 382 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
408 // it's a super call, skip 383 // it's a super call, skip
409 continue; 384 continue;
410 } 385 }
@@ -599,7 +574,9 @@ public class JarIndex {
599 // get the root node 574 // get the root node
600 List<String> ancestry = Lists.newArrayList(); 575 List<String> ancestry = Lists.newArrayList();
601 ancestry.add(obfClassEntry.getName()); 576 ancestry.add(obfClassEntry.getName());
602 ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName())); 577 for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) {
578 ancestry.add(classEntry.getName());
579 }
603 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 580 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
604 deobfuscatingTranslator, 581 deobfuscatingTranslator,
605 ancestry.get(ancestry.size() - 1) 582 ancestry.get(ancestry.size() - 1)
@@ -625,21 +602,21 @@ public class JarIndex {
625 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 602 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
626 603
627 // travel to the ancestor implementation 604 // travel to the ancestor implementation
628 String baseImplementationClassName = obfMethodEntry.getClassName(); 605 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
629 for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) { 606 for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
630 MethodEntry ancestorMethodEntry = new MethodEntry( 607 MethodEntry ancestorMethodEntry = new MethodEntry(
631 new ClassEntry(ancestorClassName), 608 new ClassEntry(ancestorClassEntry),
632 obfMethodEntry.getName(), 609 obfMethodEntry.getName(),
633 obfMethodEntry.getSignature() 610 obfMethodEntry.getSignature()
634 ); 611 );
635 if (containsObfBehavior(ancestorMethodEntry)) { 612 if (containsObfBehavior(ancestorMethodEntry)) {
636 baseImplementationClassName = ancestorClassName; 613 baseImplementationClassEntry = ancestorClassEntry;
637 } 614 }
638 } 615 }
639 616
640 // make a root node at the base 617 // make a root node at the base
641 MethodEntry methodEntry = new MethodEntry( 618 MethodEntry methodEntry = new MethodEntry(
642 new ClassEntry(baseImplementationClassName), 619 baseImplementationClassEntry,
643 obfMethodEntry.getName(), 620 obfMethodEntry.getName(),
644 obfMethodEntry.getSignature() 621 obfMethodEntry.getSignature()
645 ); 622 );
@@ -781,8 +758,8 @@ public class JarIndex {
781 public Set<String> getInterfaces(String className) { 758 public Set<String> getInterfaces(String className) {
782 Set<String> interfaceNames = new HashSet<String>(); 759 Set<String> interfaceNames = new HashSet<String>();
783 interfaceNames.addAll(m_interfaces.get(className)); 760 interfaceNames.addAll(m_interfaces.get(className));
784 for (String ancestor : m_translationIndex.getAncestry(className)) { 761 for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) {
785 interfaceNames.addAll(m_interfaces.get(ancestor)); 762 interfaceNames.addAll(m_interfaces.get(ancestor.getName()));
786 } 763 }
787 return interfaceNames; 764 return interfaceNames;
788 } 765 }
@@ -795,7 +772,7 @@ public class JarIndex {
795 String interfaceName = entry.getValue(); 772 String interfaceName = entry.getValue();
796 if (interfaceName.equals(targetInterfaceName)) { 773 if (interfaceName.equals(targetInterfaceName)) {
797 classNames.add(className); 774 classNames.add(className);
798 m_translationIndex.getSubclassNamesRecursively(classNames, className); 775 m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className));
799 } 776 }
800 } 777 }
801 return classNames; 778 return classNames;
diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index eba8d87..8718220 100644
--- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -71,9 +71,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
71 public void load(JarIndex index, boolean recurse) { 71 public void load(JarIndex index, boolean recurse) {
72 // get all the child nodes 72 // get all the child nodes
73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
74 for (String subclassName : index.getTranslationIndex().getSubclassNames(m_entry.getClassName())) { 74 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) {
75 MethodEntry methodEntry = new MethodEntry( 75 MethodEntry methodEntry = new MethodEntry(
76 new ClassEntry(subclassName), 76 subclassEntry,
77 m_entry.getName(), 77 m_entry.getName(),
78 m_entry.getSignature() 78 m_entry.getSignature()
79 ); 79 );
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java
index c14fd59..4a356eb 100644
--- a/src/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/cuchaz/enigma/analysis/TranslationIndex.java
@@ -11,97 +11,185 @@
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.io.Serializable; 13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.List; 14import java.util.List;
16import java.util.Map; 15import java.util.Map;
17import java.util.Set; 16import java.util.Set;
18 17
19import javassist.bytecode.Descriptor; 18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
20 21
21import com.google.common.collect.HashMultimap; 22import com.google.common.collect.HashMultimap;
22import com.google.common.collect.Lists; 23import com.google.common.collect.Lists;
23import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
24import com.google.common.collect.Multimap; 25import com.google.common.collect.Multimap;
25 26
27import cuchaz.enigma.mapping.ArgumentEntry;
28import cuchaz.enigma.mapping.BehaviorEntry;
29import cuchaz.enigma.mapping.ClassEntry;
30import cuchaz.enigma.mapping.Entry;
31import cuchaz.enigma.mapping.FieldEntry;
32import cuchaz.enigma.mapping.JavassistUtil;
33import cuchaz.enigma.mapping.Translator;
34
26public class TranslationIndex implements Serializable { 35public class TranslationIndex implements Serializable {
27 36
28 private static final long serialVersionUID = 738687982126844179L; 37 private static final long serialVersionUID = 738687982126844179L;
29 38
30 private Map<String,String> m_superclasses; 39 private Map<ClassEntry,ClassEntry> m_superclasses;
31 private Multimap<String,String> m_fields; 40 private Multimap<ClassEntry,FieldEntry> m_fieldEntries;
41 private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries;
32 42
33 public TranslationIndex() { 43 public TranslationIndex() {
34 m_superclasses = Maps.newHashMap(); 44 m_superclasses = Maps.newHashMap();
35 m_fields = HashMultimap.create(); 45 m_fieldEntries = HashMultimap.create();
36 } 46 m_behaviorEntries = HashMultimap.create();
37
38 public TranslationIndex(TranslationIndex other) {
39 m_superclasses = Maps.newHashMap(other.m_superclasses);
40 m_fields = HashMultimap.create(other.m_fields);
41 } 47 }
42 48
43 public void addSuperclass(String className, String superclassName) { 49 public TranslationIndex(TranslationIndex other, Translator translator) {
44 className = Descriptor.toJvmName(className);
45 superclassName = Descriptor.toJvmName(superclassName);
46 50
47 if (className.equals(superclassName)) { 51 // translate the superclasses
48 throw new IllegalArgumentException("Class cannot be its own superclass! " + className); 52 m_superclasses = Maps.newHashMap();
53 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_superclasses.entrySet()) {
54 m_superclasses.put(
55 translator.translateEntry(mapEntry.getKey()),
56 translator.translateEntry(mapEntry.getValue())
57 );
49 } 58 }
50 59
51 if (!isJre(className) && !isJre(superclassName)) { 60 // translate the fields
52 m_superclasses.put(className, superclassName); 61 m_fieldEntries = HashMultimap.create();
62 for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) {
63 m_fieldEntries.put(
64 translator.translateEntry(mapEntry.getKey()),
65 translator.translateEntry(mapEntry.getValue())
66 );
67 }
68
69 m_behaviorEntries = HashMultimap.create();
70 for (Map.Entry<ClassEntry,BehaviorEntry> mapEntry : other.m_behaviorEntries.entries()) {
71 m_behaviorEntries.put(
72 translator.translateEntry(mapEntry.getKey()),
73 translator.translateEntry(mapEntry.getValue())
74 );
53 } 75 }
54 } 76 }
55 77
56 public void addField(String className, String fieldName) { 78 public void indexClass(CtClass c) {
57 m_fields.put(className, fieldName); 79
80 ClassEntry classEntry = JavassistUtil.getClassEntry(c);
81
82 // add the superclass
83 ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c);
84 if (!isJre(classEntry) && !isJre(superclassEntry)) {
85 m_superclasses.put(classEntry, superclassEntry);
86 }
87
88 // add fields
89 for (CtField field : c.getDeclaredFields()) {
90 FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field);
91 m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
92 }
93
94 // add behaviors
95 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
96 BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior);
97 m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
98 }
58 } 99 }
59 100
60 public void renameClasses(Map<String,String> renames) { 101 public void renameClasses(Map<String,String> renames) {
61 EntryRenamer.renameClassesInMap(renames, m_superclasses); 102 EntryRenamer.renameClassesInMap(renames, m_superclasses);
62 EntryRenamer.renameClassesInMultimap(renames, m_fields); 103 EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries);
104 EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries);
63 } 105 }
64 106
65 public String getSuperclassName(String className) { 107 public ClassEntry getSuperclass(ClassEntry classEntry) {
66 return m_superclasses.get(className); 108 return m_superclasses.get(classEntry);
67 } 109 }
68 110
69 public List<String> getAncestry(String className) { 111 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
70 List<String> ancestors = new ArrayList<String>(); 112 List<ClassEntry> ancestors = Lists.newArrayList();
71 while (className != null) { 113 while (classEntry != null) {
72 className = getSuperclassName(className); 114 classEntry = getSuperclass(classEntry);
73 if (className != null) { 115 if (classEntry != null) {
74 ancestors.add(className); 116 ancestors.add(classEntry);
75 } 117 }
76 } 118 }
77 return ancestors; 119 return ancestors;
78 } 120 }
79 121
80 public List<String> getSubclassNames(String className) { 122 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
81 // linear search is fast enough for now 123 // linear search is fast enough for now
82 List<String> subclasses = Lists.newArrayList(); 124 List<ClassEntry> subclasses = Lists.newArrayList();
83 for (Map.Entry<String,String> entry : m_superclasses.entrySet()) { 125 for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) {
84 String subclass = entry.getKey(); 126 ClassEntry subclass = entry.getKey();
85 String superclass = entry.getValue(); 127 ClassEntry superclass = entry.getValue();
86 if (className.equals(superclass)) { 128 if (classEntry.equals(superclass)) {
87 subclasses.add(subclass); 129 subclasses.add(subclass);
88 } 130 }
89 } 131 }
90 return subclasses; 132 return subclasses;
91 } 133 }
92 134
93 public void getSubclassNamesRecursively(Set<String> out, String className) { 135 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
94 for (String subclassName : getSubclassNames(className)) { 136 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
95 out.add(subclassName); 137 out.add(subclassEntry);
96 getSubclassNamesRecursively(out, subclassName); 138 getSubclassesRecursively(out, subclassEntry);
139 }
140 }
141
142 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
143 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
144 out.add(subclassEntry.getName());
145 getSubclassNamesRecursively(out, subclassEntry);
97 } 146 }
98 } 147 }
99 148
100 public boolean containsField(String className, String fieldName) { 149 public boolean entryExists(Entry entry) {
101 return m_fields.containsEntry(className, fieldName); 150 if (entry instanceof FieldEntry) {
151 return fieldExists((FieldEntry)entry);
152 } else if (entry instanceof BehaviorEntry) {
153 return behaviorExists((BehaviorEntry)entry);
154 } else if (entry instanceof ArgumentEntry) {
155 return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry());
156 }
157 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
158 }
159
160 public boolean fieldExists(FieldEntry fieldEntry) {
161 return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
162 }
163
164 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
165 return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
166 }
167
168 public ClassEntry resolveEntryClass(Entry entry) {
169
170 if (entry instanceof ClassEntry) {
171 return (ClassEntry)entry;
172 }
173
174 // this entry could refer to a method on a class where the method is not actually implemented
175 // travel up the inheritance tree to find the closest implementation
176 while (!entryExists(entry)) {
177
178 // is there a parent class?
179 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
180 if (superclassEntry == null) {
181 // this is probably a method from a class in a library
182 // we can't trace the implementation up any higher unless we index the library
183 return null;
184 }
185
186 // move up to the parent class
187 entry = entry.cloneToNewClass(superclassEntry);
188 }
189 return entry.getClassEntry();
102 } 190 }
103 191
104 private boolean isJre(String className) { 192 private boolean isJre(ClassEntry classEntry) {
105 return className.startsWith("java/") || className.startsWith("javax/"); 193 return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax");
106 } 194 }
107} 195}
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java
index 735a8fa..6441830 100644
--- a/src/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -38,6 +38,7 @@ public class ClassTranslator {
38 } 38 }
39 39
40 public void translate(CtClass c) { 40 public void translate(CtClass c) {
41
41 // NOTE: the order of these translations is very important 42 // NOTE: the order of these translations is very important
42 43
43 // translate all the field and method references in the code by editing the constant pool 44 // translate all the field and method references in the code by editing the constant pool
@@ -45,7 +46,9 @@ public class ClassTranslator {
45 ConstPoolEditor editor = new ConstPoolEditor(constants); 46 ConstPoolEditor editor = new ConstPoolEditor(constants);
46 for (int i = 1; i < constants.getSize(); i++) { 47 for (int i = 1; i < constants.getSize(); i++) {
47 switch (constants.getTag(i)) { 48 switch (constants.getTag(i)) {
49
48 case ConstPool.CONST_Fieldref: { 50 case ConstPool.CONST_Fieldref: {
51
49 // translate the name 52 // translate the name
50 FieldEntry entry = new FieldEntry( 53 FieldEntry entry = new FieldEntry(
51 new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), 54 new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))),
@@ -53,6 +56,11 @@ public class ClassTranslator {
53 ); 56 );
54 FieldEntry translatedEntry = m_translator.translateEntry(entry); 57 FieldEntry translatedEntry = m_translator.translateEntry(entry);
55 58
59 // TEMP
60 if (entry.toString().equals("none/bxq.m")) {
61 System.out.println("FIELD: " + entry + " -> " + translatedEntry);
62 }
63
56 // translate the type 64 // translate the type
57 String type = constants.getFieldrefType(i); 65 String type = constants.getFieldrefType(i);
58 String translatedType = m_translator.translateSignature(type); 66 String translatedType = m_translator.translateSignature(type);
@@ -65,6 +73,7 @@ public class ClassTranslator {
65 73
66 case ConstPool.CONST_Methodref: 74 case ConstPool.CONST_Methodref:
67 case ConstPool.CONST_InterfaceMethodref: { 75 case ConstPool.CONST_InterfaceMethodref: {
76
68 // translate the name and type 77 // translate the name and type
69 BehaviorEntry entry = BehaviorEntryFactory.create( 78 BehaviorEntry entry = BehaviorEntryFactory.create(
70 Descriptor.toJvmName(editor.getMemberrefClassname(i)), 79 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java
new file mode 100644
index 0000000..b011e0b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/JavassistUtil.java
@@ -0,0 +1,55 @@
1package cuchaz.enigma.mapping;
2
3import javassist.CtBehavior;
4import javassist.CtClass;
5import javassist.CtConstructor;
6import javassist.CtField;
7import javassist.CtMethod;
8import javassist.bytecode.Descriptor;
9import cuchaz.enigma.mapping.BehaviorEntry;
10import cuchaz.enigma.mapping.ClassEntry;
11import cuchaz.enigma.mapping.ConstructorEntry;
12import cuchaz.enigma.mapping.FieldEntry;
13import cuchaz.enigma.mapping.MethodEntry;
14
15public class JavassistUtil {
16
17 public static ClassEntry getClassEntry(CtClass c) {
18 return new ClassEntry(Descriptor.toJvmName(c.getName()));
19 }
20
21 public static ClassEntry getSuperclassEntry(CtClass c) {
22 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
23 }
24
25 public static MethodEntry getMethodEntry(CtMethod method) {
26 return new MethodEntry(
27 getClassEntry(method.getDeclaringClass()),
28 method.getName(),
29 method.getMethodInfo().getDescriptor()
30 );
31 }
32
33 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
34 return new ConstructorEntry(
35 getClassEntry(constructor.getDeclaringClass()),
36 constructor.getMethodInfo().getDescriptor()
37 );
38 }
39
40 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
41 if (behavior instanceof CtMethod) {
42 return getMethodEntry((CtMethod)behavior);
43 } else if (behavior instanceof CtConstructor) {
44 return getConstructorEntry((CtConstructor)behavior);
45 }
46 throw new Error("behavior is neither Method nor Constructor!");
47 }
48
49 public static FieldEntry getFieldEntry(CtField field) {
50 return new FieldEntry(
51 getClassEntry(field.getDeclaringClass()),
52 field.getName()
53 );
54 }
55}
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java
index c5e38f4..cc560a8 100644
--- a/src/cuchaz/enigma/mapping/Mappings.java
+++ b/src/cuchaz/enigma/mapping/Mappings.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Maps;
24import com.google.common.collect.Sets; 24import com.google.common.collect.Sets;
25 25
26import cuchaz.enigma.Util; 26import cuchaz.enigma.Util;
27import cuchaz.enigma.analysis.TranslationIndex;
27import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 28import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
28 29
29public class Mappings implements Serializable { 30public class Mappings implements Serializable {
@@ -104,11 +105,11 @@ public class Mappings implements Serializable {
104 return m_classesByDeobf.get(deobfName); 105 return m_classesByDeobf.get(deobfName);
105 } 106 }
106 107
107 public Translator getTranslator(TranslationDirection direction) { 108 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
108 switch (direction) { 109 switch (direction) {
109 case Deobfuscating: 110 case Deobfuscating:
110 111
111 return new Translator(direction, m_classesByObf); 112 return new Translator(direction, m_classesByObf, index);
112 113
113 case Obfuscating: 114 case Obfuscating:
114 115
@@ -122,7 +123,11 @@ public class Mappings implements Serializable {
122 } 123 }
123 } 124 }
124 125
125 return new Translator(direction, classes); 126 // translate the translation index
127 // NOTE: this isn't actually recursive
128 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index));
129
130 return new Translator(direction, classes, deobfIndex);
126 131
127 default: 132 default:
128 throw new Error("Invalid translation direction!"); 133 throw new Error("Invalid translation direction!");
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java
index cb95f42..3aac65a 100644
--- a/src/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -100,10 +100,10 @@ public class MappingsRenamer {
100 100
101 deobfName = NameValidator.validateMethodName(deobfName); 101 deobfName = NameValidator.validateMethodName(deobfName);
102 for (MethodEntry entry : implementations) { 102 for (MethodEntry entry : implementations) {
103 String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateSignature(obf.getSignature()); 103 String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature());
104 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); 104 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature);
105 if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { 105 if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
106 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(entry.getClassName()); 106 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName());
107 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 107 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
108 } 108 }
109 } 109 }
@@ -117,7 +117,7 @@ public class MappingsRenamer {
117 deobfName = NameValidator.validateMethodName(deobfName); 117 deobfName = NameValidator.validateMethodName(deobfName);
118 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 118 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature());
119 if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { 119 if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
120 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(obf.getClassName()); 120 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName());
121 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 121 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
122 } 122 }
123 123
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
index d8d9f48..a5a3e2f 100644
--- a/src/cuchaz/enigma/mapping/Translator.java
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -14,21 +14,24 @@ import java.util.Map;
14 14
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16 16
17import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 18import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
18 19
19public class Translator { 20public class Translator {
20 21
21 private TranslationDirection m_direction; 22 private TranslationDirection m_direction;
22 private Map<String,ClassMapping> m_classes; 23 private Map<String,ClassMapping> m_classes;
24 private TranslationIndex m_index;
23 25
24 public Translator() { 26 public Translator() {
25 m_direction = null; 27 m_direction = null;
26 m_classes = Maps.newHashMap(); 28 m_classes = Maps.newHashMap();
27 } 29 }
28 30
29 public Translator(TranslationDirection direction, Map<String,ClassMapping> classes) { 31 public Translator(TranslationDirection direction, Map<String,ClassMapping> classes, TranslationIndex index) {
30 m_direction = direction; 32 m_direction = direction;
31 m_classes = classes; 33 m_classes = classes;
34 m_index = index;
32 } 35 }
33 36
34 @SuppressWarnings("unchecked") 37 @SuppressWarnings("unchecked")
@@ -100,17 +103,22 @@ public class Translator {
100 103
101 public String translate(FieldEntry in) { 104 public String translate(FieldEntry in) {
102 105
103 // look for the class 106 // resolve the class entry
104 ClassMapping classMapping = findClassMapping(in.getClassEntry()); 107 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
105 if (classMapping != null) { 108 if (resolvedClassEntry != null) {
106 109
107 // look for the field 110 // look for the class
108 String translatedName = m_direction.choose( 111 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
109 classMapping.getDeobfFieldName(in.getName()), 112 if (classMapping != null) {
110 classMapping.getObfFieldName(in.getName()) 113
111 ); 114 // look for the field
112 if (translatedName != null) { 115 String translatedName = m_direction.choose(
113 return translatedName; 116 classMapping.getDeobfFieldName(in.getName()),
117 classMapping.getObfFieldName(in.getName())
118 );
119 if (translatedName != null) {
120 return translatedName;
121 }
114 } 122 }
115 } 123 }
116 return null; 124 return null;
@@ -126,15 +134,22 @@ public class Translator {
126 134
127 public String translate(MethodEntry in) { 135 public String translate(MethodEntry in) {
128 136
129 // look for class 137 // resolve the class entry
130 ClassMapping classMapping = findClassMapping(in.getClassEntry()); 138 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
131 if (classMapping != null) { 139 if (resolvedClassEntry != null) {
132 140
133 // look for the method 141 // look for class
134 MethodMapping methodMapping = m_direction.choose(classMapping.getMethodByObf(in.getName(), in.getSignature()), 142 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
135 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))); 143 if (classMapping != null) {
136 if (methodMapping != null) { 144
137 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); 145 // look for the method
146 MethodMapping methodMapping = m_direction.choose(
147 classMapping.getMethodByObf(in.getName(), in.getSignature()),
148 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
149 );
150 if (methodMapping != null) {
151 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
152 }
138 } 153 }
139 } 154 }
140 return null; 155 return null;