summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java11
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BridgeMarker.java38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java47
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java26
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java23
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java50
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java123
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java588
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java9
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java12
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java (renamed from src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java)38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java72
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java36
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java)83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java123
20 files changed, 511 insertions, 850 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index 547d85e..8181418 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -11,8 +11,7 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import javassist.CtBehavior; 14import cuchaz.enigma.bytecode.AccessFlags;
15import javassist.CtField;
16 15
17import java.lang.reflect.Modifier; 16import java.lang.reflect.Modifier;
18 17
@@ -20,12 +19,8 @@ public enum Access {
20 19
21 PUBLIC, PROTECTED, PACKAGE, PRIVATE; 20 PUBLIC, PROTECTED, PACKAGE, PRIVATE;
22 21
23 public static Access get(CtBehavior behavior) { 22 public static Access get(AccessFlags flags) {
24 return get(behavior.getModifiers()); 23 return get(flags.getFlags());
25 }
26
27 public static Access get(CtField field) {
28 return get(field.getModifiers());
29 } 24 }
30 25
31 public static Access get(int modifiers) { 26 public static Access get(int modifiers) {
diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
deleted file mode 100644
index a2f1f90..0000000
--- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
+++ /dev/null
@@ -1,38 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.mapping.EntryFactory;
15import cuchaz.enigma.mapping.MethodEntry;
16import javassist.CtClass;
17import javassist.CtMethod;
18import javassist.bytecode.AccessFlag;
19
20public class BridgeMarker {
21
22 public static void markBridges(JarIndex jarIndex, CtClass c) {
23
24 for (CtMethod method : c.getDeclaredMethods()) {
25 MethodEntry methodEntry = EntryFactory.getMethodEntry(method);
26
27 // is this a bridge method?
28 MethodEntry bridgedMethodEntry = jarIndex.getBridgedMethod(methodEntry);
29 if (bridgedMethodEntry != null) {
30
31 // it's a bridge method! add the bridge flag
32 int flags = method.getMethodInfo().getAccessFlags();
33 flags |= AccessFlag.BRIDGE;
34 method.getMethodInfo().setAccessFlags(flags);
35 }
36 }
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index f2fb2f8..d0e1ddb 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -21,8 +21,8 @@ import java.util.List;
21 21
22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
23 23
24 private Translator deobfuscatingTranslator; 24 private final Translator deobfuscatingTranslator;
25 private ClassEntry entry; 25 private final ClassEntry entry;
26 26
27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
@@ -31,7 +31,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
31 31
32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
33 // is this the node? 33 // is this the node?
34 if (node.entry.equals(entry.getClassEntry())) { 34 if (node.entry.equals(entry.getOwnerClassEntry())) {
35 return node; 35 return node;
36 } 36 }
37 37
@@ -50,7 +50,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
50 } 50 }
51 51
52 public String getDeobfClassName() { 52 public String getDeobfClassName() {
53 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 53 return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName();
54 } 54 }
55 55
56 @Override 56 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index 24e7cb0..8fd71b7 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -20,12 +20,12 @@ import java.util.List;
20 20
21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
22 22
23 private Translator deobfuscatingTranslator; 23 private final Translator deobfuscatingTranslator;
24 private String obfClassName; 24 private final ClassEntry obfClassEntry;
25 25
26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.deobfuscatingTranslator = deobfuscatingTranslator;
28 this.obfClassName = obfClassName; 28 this.obfClassEntry = new ClassEntry(obfClassName);
29 } 29 }
30 30
31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
@@ -45,11 +45,11 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
45 } 45 }
46 46
47 public String getObfClassName() { 47 public String getObfClassName() {
48 return this.obfClassName; 48 return this.obfClassEntry.getClassName();
49 } 49 }
50 50
51 public String getDeobfClassName() { 51 public String getDeobfClassName() {
52 return this.deobfuscatingTranslator.translateClass(this.obfClassName); 52 return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName();
53 } 53 }
54 54
55 @Override 55 @Override
@@ -58,13 +58,13 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
58 if (deobfClassName != null) { 58 if (deobfClassName != null) {
59 return deobfClassName; 59 return deobfClassName;
60 } 60 }
61 return this.obfClassName; 61 return this.obfClassEntry.getName();
62 } 62 }
63 63
64 public void load(TranslationIndex ancestries, boolean recurse) { 64 public void load(TranslationIndex ancestries, boolean recurse) {
65 // get all the child nodes 65 // get all the child nodes
66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
67 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { 67 for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) {
68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); 68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
69 } 69 }
70 70
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index 3761fca..b972585 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -12,8 +12,8 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
15import cuchaz.enigma.mapping.ConstructorEntry;
16import cuchaz.enigma.mapping.Entry; 15import cuchaz.enigma.mapping.Entry;
16import cuchaz.enigma.mapping.MethodEntry;
17import cuchaz.enigma.utils.Utils; 17import cuchaz.enigma.utils.Utils;
18 18
19import java.util.Arrays; 19import java.util.Arrays;
@@ -21,7 +21,7 @@ import java.util.List;
21 21
22public class EntryReference<E extends Entry, C extends Entry> { 22public class EntryReference<E extends Entry, C extends Entry> {
23 23
24 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); 24 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static");
25 public E entry; 25 public E entry;
26 public C context; 26 public C context;
27 27
@@ -40,7 +40,7 @@ public class EntryReference<E extends Entry, C extends Entry> {
40 this.context = context; 40 this.context = context;
41 41
42 this.sourceName = sourceName != null && !sourceName.isEmpty(); 42 this.sourceName = sourceName != null && !sourceName.isEmpty();
43 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { 43 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) {
44 this.sourceName = false; 44 this.sourceName = false;
45 } 45 }
46 } 46 }
@@ -53,9 +53,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
53 53
54 public ClassEntry getLocationClassEntry() { 54 public ClassEntry getLocationClassEntry() {
55 if (context != null) { 55 if (context != null) {
56 return context.getClassEntry(); 56 return context.getOwnerClassEntry();
57 } 57 }
58 return entry.getClassEntry(); 58 return entry.getOwnerClassEntry();
59 } 59 }
60 60
61 public boolean isNamed() { 61 public boolean isNamed() {
@@ -63,9 +63,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
63 } 63 }
64 64
65 public Entry getNameableEntry() { 65 public Entry getNameableEntry() {
66 if (entry instanceof ConstructorEntry) { 66 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) {
67 // renaming a constructor really means renaming the class 67 // renaming a constructor really means renaming the class
68 return entry.getClassEntry(); 68 return entry.getOwnerClassEntry();
69 } 69 }
70 return entry; 70 return entry;
71 } 71 }
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index 75806c3..b0bcc91 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -87,18 +87,18 @@ public class EntryRenamer {
87 MethodEntry newMethodEntry = renames.get(methodEntry); 87 MethodEntry newMethodEntry = renames.get(methodEntry);
88 if (newMethodEntry != null) { 88 if (newMethodEntry != null) {
89 return (T) new MethodEntry( 89 return (T) new MethodEntry(
90 methodEntry.getClassEntry(), 90 methodEntry.getOwnerClassEntry(),
91 newMethodEntry.getName(), 91 newMethodEntry.getName(),
92 methodEntry.getSignature() 92 methodEntry.getDesc()
93 ); 93 );
94 } 94 }
95 return thing; 95 return thing;
96 } else if (thing instanceof ArgumentEntry) { 96 } else if (thing instanceof LocalVariableEntry) {
97 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 97 LocalVariableEntry variableEntry = (LocalVariableEntry) thing;
98 return (T) new ArgumentEntry( 98 return (T) new LocalVariableEntry(
99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), 99 renameMethodsInThing(renames, variableEntry.getOwnerEntry()),
100 argumentEntry.getIndex(), 100 variableEntry.getIndex(),
101 argumentEntry.getName() 101 variableEntry.getName()
102 ); 102 );
103 } else if (thing instanceof EntryReference) { 103 } else if (thing instanceof EntryReference) {
104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
@@ -119,27 +119,24 @@ public class EntryRenamer {
119 } else if (thing instanceof ClassEntry) { 119 } else if (thing instanceof ClassEntry) {
120 ClassEntry classEntry = (ClassEntry) thing; 120 ClassEntry classEntry = (ClassEntry) thing;
121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); 121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
122 } else if (thing instanceof FieldEntry) { 122 } else if (thing instanceof FieldDefEntry) {
123 FieldEntry fieldEntry = (FieldEntry) thing; 123 FieldDefEntry fieldEntry = (FieldDefEntry) thing;
124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); 124 return (T) new FieldDefEntry(renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getDesc()), fieldEntry.getAccess());
125 } else if (thing instanceof ConstructorEntry) { 125 } else if (thing instanceof MethodDefEntry) {
126 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 126 MethodDefEntry methodEntry = (MethodDefEntry) thing;
127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); 127 return (T) new MethodDefEntry(renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getDesc()), methodEntry.getAccess());
128 } else if (thing instanceof MethodEntry) { 128 } else if (thing instanceof LocalVariableEntry) {
129 MethodEntry methodEntry = (MethodEntry) thing; 129 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); 130 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName());
131 } else if (thing instanceof ArgumentEntry) {
132 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName());
134 } else if (thing instanceof EntryReference) { 131 } else if (thing instanceof EntryReference) {
135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 132 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
136 reference.entry = renameClassesInThing(renames, reference.entry); 133 reference.entry = renameClassesInThing(renames, reference.entry);
137 reference.context = renameClassesInThing(renames, reference.context); 134 reference.context = renameClassesInThing(renames, reference.context);
138 return thing; 135 return thing;
139 } else if (thing instanceof Signature) { 136 } else if (thing instanceof MethodDescriptor) {
140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); 137 return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
141 } else if (thing instanceof Type) { 138 } else if (thing instanceof TypeDescriptor) {
142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); 139 return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
143 } 140 }
144 141
145 return thing; 142 return thing;
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 34d2eff..3e467db 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -11,17 +11,15 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.*;
15import cuchaz.enigma.mapping.FieldEntry;
16import cuchaz.enigma.mapping.Translator;
17 15
18import javax.swing.tree.DefaultMutableTreeNode; 16import javax.swing.tree.DefaultMutableTreeNode;
19 17
20public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 18public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
21 19
22 private Translator deobfuscatingTranslator; 20 private Translator deobfuscatingTranslator;
23 private FieldEntry entry; 21 private FieldEntry entry;
24 private EntryReference<FieldEntry, BehaviorEntry> reference; 22 private EntryReference<FieldEntry, MethodDefEntry> reference;
25 private Access access; 23 private Access access;
26 24
27 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 25 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) {
@@ -30,7 +28,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
30 this.reference = null; 28 this.reference = null;
31 } 29 }
32 30
33 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { 31 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, Access access) {
34 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
35 this.entry = reference.entry; 33 this.entry = reference.entry;
36 this.reference = reference; 34 this.reference = reference;
@@ -43,34 +41,34 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
43 } 41 }
44 42
45 @Override 43 @Override
46 public EntryReference<FieldEntry, BehaviorEntry> getReference() { 44 public EntryReference<FieldEntry, MethodDefEntry> getReference() {
47 return this.reference; 45 return this.reference;
48 } 46 }
49 47
50 @Override 48 @Override
51 public String toString() { 49 public String toString() {
52 if (this.reference != null) { 50 if (this.reference != null) {
53 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); 51 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access);
54 } 52 }
55 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 53 return deobfuscatingTranslator.getTranslatedField(entry).getName();
56 } 54 }
57 55
58 public void load(JarIndex index, boolean recurse) { 56 public void load(JarIndex index, boolean recurse) {
59 // get all the child nodes 57 // get all the child nodes
60 if (this.reference == null) { 58 if (this.reference == null) {
61 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { 59 for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) {
62 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 60 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
63 } 61 }
64 } else { 62 } else {
65 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { 63 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.reference.context)) {
66 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 64 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
67 } 65 }
68 } 66 }
69 67
70 if (recurse && children != null) { 68 if (recurse && children != null) {
71 for (Object node : children) { 69 for (Object node : children) {
72 if (node instanceof BehaviorReferenceTreeNode) { 70 if (node instanceof MethodReferenceTreeNode) {
73 ((BehaviorReferenceTreeNode) node).load(index, true); 71 ((MethodReferenceTreeNode) node).load(index, true);
74 } else if (node instanceof FieldReferenceTreeNode) { 72 } else if (node instanceof FieldReferenceTreeNode) {
75 ((FieldReferenceTreeNode) node).load(index, true); 73 ((FieldReferenceTreeNode) node).load(index, true);
76 } 74 }
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
new file mode 100644
index 0000000..97d6ffa
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
@@ -0,0 +1,38 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.ClassDefEntry;
4import org.objectweb.asm.ClassVisitor;
5import org.objectweb.asm.FieldVisitor;
6import org.objectweb.asm.MethodVisitor;
7
8public class IndexClassVisitor extends ClassVisitor {
9 private final JarIndex index;
10 private ClassDefEntry classEntry;
11
12 public IndexClassVisitor(JarIndex index, int api) {
13 super(api);
14 this.index = index;
15 }
16
17 @Override
18 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
19 this.classEntry = this.index.indexClass(access, name, superName, interfaces);
20 super.visit(version, access, name, signature, superName, interfaces);
21 }
22
23 @Override
24 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
25 if (this.classEntry != null) {
26 this.index.indexField(this.classEntry, access, name, desc);
27 }
28 return super.visitField(access, name, desc, signature, value);
29 }
30
31 @Override
32 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
33 if (this.classEntry != null) {
34 this.index.indexMethod(this.classEntry, access, name, desc);
35 }
36 return super.visitMethod(access, name, desc, signature, exceptions);
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
new file mode 100644
index 0000000..621bd33
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
@@ -0,0 +1,23 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.ClassEntry;
4import org.objectweb.asm.ClassVisitor;
5
6public class IndexInnerClassVisitor extends ClassVisitor {
7 private final JarIndex index;
8
9 public IndexInnerClassVisitor(JarIndex index, int api) {
10 super(api);
11 this.index = index;
12 }
13
14 @Override
15 public void visitInnerClass(String name, String outerName, String innerName, int access) {
16 ClassEntry entry = new ClassEntry(name);
17 // Ignore anonymous classes
18 if (innerName != null && outerName != null) {
19 ClassEntry outerEntry = new ClassEntry(outerName);
20 index.indexInnerClass(entry, outerEntry);
21 }
22 }
23}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
new file mode 100644
index 0000000..552601b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
@@ -0,0 +1,50 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4import cuchaz.enigma.mapping.*;
5import org.objectweb.asm.ClassVisitor;
6import org.objectweb.asm.MethodVisitor;
7
8public class IndexReferenceVisitor extends ClassVisitor {
9 private final JarIndex index;
10 private final ReferencedEntryPool entryPool;
11 private ClassEntry classEntry;
12
13 public IndexReferenceVisitor(JarIndex index, ReferencedEntryPool entryPool, int api) {
14 super(api);
15 this.index = index;
16 this.entryPool = entryPool;
17 }
18
19 @Override
20 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
21 this.classEntry = new ClassEntry(name);
22 }
23
24 @Override
25 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
26 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), new AccessFlags(access));
27 return new Method(this.index, entry, this.api);
28 }
29
30 private class Method extends MethodVisitor {
31 private final JarIndex index;
32 private final MethodDefEntry callerEntry;
33
34 public Method(JarIndex index, MethodDefEntry callerEntry, int api) {
35 super(api);
36 this.index = index;
37 this.callerEntry = callerEntry;
38 }
39
40 @Override
41 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
42 this.index.indexFieldAccess(callerEntry, owner, name, desc);
43 }
44
45 @Override
46 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
47 this.index.indexMethodCall(callerEntry, owner, name, desc);
48 }
49 }
50}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
deleted file mode 100644
index 87d3797..0000000
--- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
+++ /dev/null
@@ -1,123 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.Constants;
16import cuchaz.enigma.mapping.ClassEntry;
17import javassist.ByteArrayClassPath;
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.Descriptor;
22
23import java.io.ByteArrayOutputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.Enumeration;
27import java.util.Iterator;
28import java.util.List;
29import java.util.jar.JarEntry;
30import java.util.jar.JarFile;
31
32public class JarClassIterator implements Iterator<CtClass> {
33
34 private JarFile jar;
35 private Iterator<JarEntry> iter;
36
37 public JarClassIterator(JarFile jar) {
38 this.jar = jar;
39
40 // get the jar entries that correspond to classes
41 List<JarEntry> classEntries = Lists.newArrayList();
42 Enumeration<JarEntry> entries = this.jar.entries();
43 while (entries.hasMoreElements()) {
44 JarEntry entry = entries.nextElement();
45
46 // is this a class file?
47 if (entry.getName().endsWith(".class")) {
48 classEntries.add(entry);
49 }
50 }
51 this.iter = classEntries.iterator();
52 }
53
54 public static List<ClassEntry> getClassEntries(JarFile jar) {
55 List<ClassEntry> classEntries = Lists.newArrayList();
56 Enumeration<JarEntry> entries = jar.entries();
57 while (entries.hasMoreElements()) {
58 JarEntry entry = entries.nextElement();
59
60 // is this a class file?
61 if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
62 classEntries.add(getClassEntry(entry));
63 }
64 }
65 return classEntries;
66 }
67
68 public static Iterable<CtClass> classes(final JarFile jar) {
69 return () -> new JarClassIterator(jar);
70 }
71
72 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException {
73 // read the class into a buffer
74 ByteArrayOutputStream bos = new ByteArrayOutputStream();
75 byte[] buf = new byte[Constants.KiB];
76 int totalNumBytesRead = 0;
77 InputStream in = jar.getInputStream(entry);
78 while (in.available() > 0) {
79 int numBytesRead = in.read(buf);
80 if (numBytesRead < 0) {
81 break;
82 }
83 bos.write(buf, 0, numBytesRead);
84
85 // sanity checking
86 totalNumBytesRead += numBytesRead;
87 if (totalNumBytesRead > Constants.MiB) {
88 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!");
89 }
90 }
91
92 // get a javassist handle for the class
93 String className = Descriptor.toJavaName(getClassEntry(entry).getName());
94 ClassPool classPool = new ClassPool();
95 classPool.appendSystemPath();
96 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray()));
97 return classPool.get(className);
98 }
99
100 private static ClassEntry getClassEntry(JarEntry entry) {
101 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length()));
102 }
103
104 @Override
105 public boolean hasNext() {
106 return this.iter.hasNext();
107 }
108
109 @Override
110 public CtClass next() {
111 JarEntry entry = this.iter.next();
112 try {
113 return getClass(this.jar, entry);
114 } catch (IOException | NotFoundException ex) {
115 throw new Error("Unable to load class: " + entry.getName());
116 }
117 }
118
119 @Override
120 public void remove() {
121 throw new UnsupportedOperationException();
122 }
123}
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 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.*; 14import com.google.common.collect.*;
15import cuchaz.enigma.bytecode.AccessFlags;
15import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.Translator; 17import org.objectweb.asm.Opcodes;
17import javassist.*;
18import javassist.bytecode.*;
19import javassist.expr.*;
20 18
21import java.lang.reflect.Modifier;
22import java.util.*; 19import java.util.*;
23import java.util.jar.JarFile;
24 20
25public class JarIndex { 21public class JarIndex {
26 22
23 private final ReferencedEntryPool entryPool;
24
27 private Set<ClassEntry> obfClassEntries; 25 private Set<ClassEntry> obfClassEntries;
28 private TranslationIndex translationIndex; 26 private TranslationIndex translationIndex;
29 private Map<Entry, Access> access; 27 private Map<Entry, Access> access;
30 private Multimap<ClassEntry, FieldEntry> fields; 28 private Multimap<ClassEntry, FieldDefEntry> fields;
31 private Multimap<ClassEntry, BehaviorEntry> behaviors; 29 private Multimap<ClassEntry, MethodDefEntry> methods;
32 private Multimap<String, MethodEntry> methodImplementations; 30 private Multimap<String, MethodDefEntry> methodImplementations;
33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; 31 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodReferences;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; 32 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 33 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
36 private Map<ClassEntry, ClassEntry> outerClassesByInner; 34 private Map<ClassEntry, ClassEntry> outerClassesByInner;
37 private Map<ClassEntry, BehaviorEntry> anonymousClasses;
38 private Map<MethodEntry, MethodEntry> bridgedMethods; 35 private Map<MethodEntry, MethodEntry> bridgedMethods;
39 private Set<MethodEntry> syntheticMethods; 36 private Set<MethodEntry> syntheticMethods;
40 37
41 public JarIndex() { 38 public JarIndex(ReferencedEntryPool entryPool) {
39 this.entryPool = entryPool;
42 this.obfClassEntries = Sets.newHashSet(); 40 this.obfClassEntries = Sets.newHashSet();
43 this.translationIndex = new TranslationIndex(); 41 this.translationIndex = new TranslationIndex(entryPool);
44 this.access = Maps.newHashMap(); 42 this.access = Maps.newHashMap();
45 this.fields = HashMultimap.create(); 43 this.fields = HashMultimap.create();
46 this.behaviors = HashMultimap.create(); 44 this.methods = HashMultimap.create();
47 this.methodImplementations = HashMultimap.create(); 45 this.methodImplementations = HashMultimap.create();
48 this.behaviorReferences = HashMultimap.create(); 46 this.methodReferences = HashMultimap.create();
49 this.fieldReferences = HashMultimap.create(); 47 this.fieldReferences = HashMultimap.create();
50 this.innerClassesByOuter = HashMultimap.create(); 48 this.innerClassesByOuter = HashMultimap.create();
51 this.outerClassesByInner = Maps.newHashMap(); 49 this.outerClassesByInner = Maps.newHashMap();
52 this.anonymousClasses = Maps.newHashMap();
53 this.bridgedMethods = Maps.newHashMap(); 50 this.bridgedMethods = Maps.newHashMap();
54 this.syntheticMethods = Sets.newHashSet(); 51 this.syntheticMethods = Sets.newHashSet();
55 } 52 }
56 53
57 public void indexJar(JarFile jar, boolean buildInnerClasses) { 54 public void indexJar(ParsedJar jar, boolean buildInnerClasses) {
58 55
59 // step 1: read the class names 56 // step 1: read the class names
60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); 57 obfClassEntries.addAll(jar.getClassEntries());
61
62 // step 2: index field/method/constructor access
63 for (CtClass c : JarClassIterator.classes(jar)) {
64 for (CtField field : c.getDeclaredFields()) {
65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
66 this.access.put(fieldEntry, Access.get(field));
67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry);
68 }
69 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
71 this.access.put(behaviorEntry, Access.get(behavior));
72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry);
73 }
74 }
75 58
76 // step 3: index extends, implements, fields, and methods 59 // step 2: index classes, fields, methods, interfaces
77 for (CtClass c : JarClassIterator.classes(jar)) { 60 jar.visit(node -> node.accept(new IndexClassVisitor(this, Opcodes.ASM5)));
78 this.translationIndex.indexClass(c);
79 String className = Descriptor.toJvmName(c.getName());
80 for (String interfaceName : c.getClassFile().getInterfaces()) {
81 className = Descriptor.toJvmName(className);
82 interfaceName = Descriptor.toJvmName(interfaceName);
83 if (className.equals(interfaceName)) {
84 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
85 }
86 }
87 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
88 indexBehavior(behavior);
89 }
90 }
91 61
92 // step 4: index field, method, constructor references 62 // step 3: index field, method, constructor references
93 for (CtClass c : JarClassIterator.classes(jar)) { 63 jar.visit(node -> node.accept(new IndexReferenceVisitor(this, entryPool, Opcodes.ASM5)));
94 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 64
95 indexBehaviorReferences(behavior); 65 // step 4: index bridged methods
66 for (MethodDefEntry methodEntry : methods.values()) {
67 // look for bridge and bridged methods
68 MethodEntry bridgedMethod = findBridgedMethod(methodEntry);
69 if (bridgedMethod != null) {
70 this.bridgedMethods.put(methodEntry, bridgedMethod);
96 } 71 }
97 } 72 }
98 73
99 if (buildInnerClasses) { 74 if (buildInnerClasses) {
100
101 // step 5: index inner classes and anonymous classes 75 // step 5: index inner classes and anonymous classes
102 for (CtClass c : JarClassIterator.classes(jar)) { 76 jar.visit(node -> node.accept(new IndexInnerClassVisitor(this, Opcodes.ASM5)));
103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c);
104 ClassEntry outerClassEntry = findOuterClass(c);
105 if (outerClassEntry != null) {
106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry);
107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null;
108 assert (innerWasAdded);
109
110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry);
111 if (enclosingBehavior != null) {
112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior);
113
114 // DEBUG
115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
116 }/* else {
117 // DEBUG
118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
119 }*/
120 }
121 }
122 77
123 // step 6: update other indices with inner class info 78 // step 6: update other indices with inner class info
124 Map<String, String> renames = Maps.newHashMap(); 79 Map<String, String> renames = Maps.newHashMap();
@@ -133,385 +88,109 @@ public class JarIndex {
133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 88 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
134 this.translationIndex.renameClasses(renames); 89 this.translationIndex.renameClasses(renames);
135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 90 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); 91 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 92 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
138 EntryRenamer.renameClassesInMap(renames, this.access); 93 EntryRenamer.renameClassesInMap(renames, this.access);
139 } 94 }
140 } 95 }
141 96
142 private void indexBehavior(CtBehavior behavior) { 97 protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) {
143 // get the behavior entry 98 for (String interfaceName : interfaces) {
144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 99 if (name.equals(interfaceName)) {
145 if (behaviorEntry instanceof MethodEntry) { 100 throw new IllegalArgumentException("Class cannot be its own interface! " + name);
146 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
147
148 // is synthetic
149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) {
150 syntheticMethods.add(methodEntry);
151 }
152
153 // index implementation
154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
155
156 // look for bridge and bridged methods
157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior);
158 if (bridgedMethod != null) {
159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod));
160 } 101 }
161 } 102 }
162 // looks like we don't care about constructors here 103 return this.translationIndex.indexClass(access, name, superName, interfaces);
163 }
164
165 private void indexBehaviorReferences(CtBehavior behavior) {
166 // index method calls
167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
168 try {
169 behavior.instrument(new ExprEditor() {
170 @Override
171 public void edit(MethodCall call) {
172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call);
173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry);
174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
175 calledMethodEntry = new MethodEntry(
176 resolvedClassEntry,
177 calledMethodEntry.getName(),
178 calledMethodEntry.getSignature()
179 );
180 }
181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
182 calledMethodEntry,
183 call.getMethodName(),
184 behaviorEntry
185 );
186 behaviorReferences.put(calledMethodEntry, reference);
187 }
188
189 @Override
190 public void edit(FieldAccess call) {
191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call);
192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry);
193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry);
195 }
196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>(
197 calledFieldEntry,
198 call.getFieldName(),
199 behaviorEntry
200 );
201 fieldReferences.put(calledFieldEntry, reference);
202 }
203
204 @Override
205 public void edit(ConstructorCall call) {
206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
208 calledConstructorEntry,
209 call.getMethodName(),
210 behaviorEntry
211 );
212 behaviorReferences.put(calledConstructorEntry, reference);
213 }
214
215 @Override
216 public void edit(NewExpr call) {
217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
219 calledConstructorEntry,
220 call.getClassName(),
221 behaviorEntry
222 );
223 behaviorReferences.put(calledConstructorEntry, reference);
224 }
225 });
226 } catch (CannotCompileException ex) {
227 throw new Error(ex);
228 }
229 } 104 }
230 105
231 private CtMethod getBridgedMethod(CtMethod method) { 106 protected void indexField(ClassDefEntry owner, int access, String name, String desc) {
232 107 FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), new AccessFlags(access));
233 // bridge methods just call another method, cast it to the return type, and return the result 108 this.translationIndex.indexField(fieldEntry);
234 // let's see if we can detect this scenario 109 this.access.put(fieldEntry, Access.get(access));
235 110 this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
236 // skip non-synthetic methods
237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
238 return null;
239 }
240
241 // get all the called methods
242 final List<MethodCall> methodCalls = Lists.newArrayList();
243 try {
244 method.instrument(new ExprEditor() {
245 @Override
246 public void edit(MethodCall call) {
247 methodCalls.add(call);
248 }
249 });
250 } catch (CannotCompileException ex) {
251 // this is stupid... we're not even compiling anything
252 throw new Error(ex);
253 }
254
255 // is there just one?
256 if (methodCalls.size() != 1) {
257 return null;
258 }
259 MethodCall call = methodCalls.get(0);
260
261 try {
262 // we have a bridge method!
263 return call.getMethod();
264 } catch (NotFoundException ex) {
265 // can't find the type? not a bridge method
266 return null;
267 }
268 } 111 }
269 112
270 private ClassEntry findOuterClass(CtClass c) { 113 protected void indexMethod(ClassDefEntry owner, int access, String name, String desc) {
271 114 MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), new AccessFlags(access));
272 ClassEntry classEntry = EntryFactory.getClassEntry(c); 115 this.translationIndex.indexMethod(methodEntry);
116 this.access.put(methodEntry, Access.get(access));
117 this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry);
273 118
274 // does this class already have an outer class? 119 if (new AccessFlags(access).isSynthetic()) {
275 if (classEntry.isInnerClass()) { 120 syntheticMethods.add(methodEntry);
276 return classEntry.getOuterClassEntry();
277 } 121 }
278 122
279 // inner classes: 123 // we don't care about constructors here
280 // have constructors that can (illegally) set synthetic fields 124 if (!methodEntry.isConstructor()) {
281 // the outer class is the only class that calls constructors 125 // index implementation
282 126 this.methodImplementations.put(methodEntry.getClassName(), methodEntry);
283 // use the synthetic fields to find the synthetic constructors
284 for (CtConstructor constructor : c.getDeclaredConstructors()) {
285 Set<String> syntheticFieldTypes = Sets.newHashSet();
286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
287 continue;
288 }
289
290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
291
292 // gather the classes from the illegally-set synthetic fields
293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
294 for (String type : syntheticFieldTypes) {
295 if (type.startsWith("L")) {
296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
297 if (isSaneOuterClass(outerClassEntry, classEntry)) {
298 illegallySetClasses.add(outerClassEntry);
299 }
300 }
301 }
302
303 // who calls this constructor?
304 Set<ClassEntry> callerClasses = Sets.newHashSet();
305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
306
307 // make sure it's not a call to super
308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
309
310 // is the entry a superclass of the context?
311 ClassEntry calledClassEntry = reference.entry.getClassEntry();
312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry());
313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
314 // it's a super call, skip
315 continue;
316 }
317 }
318
319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
320 callerClasses.add(reference.context.getClassEntry());
321 }
322 }
323
324 // do we have an answer yet?
325 if (callerClasses.isEmpty()) {
326 if (illegallySetClasses.size() == 1) {
327 return illegallySetClasses.iterator().next();
328 } else {
329 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
330 }
331 } else {
332 if (callerClasses.size() == 1) {
333 return callerClasses.iterator().next();
334 } else {
335 // multiple callers, do the illegally set classes narrow it down?
336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
337 intersection.retainAll(illegallySetClasses);
338 if (intersection.size() == 1) {
339 return intersection.iterator().next();
340 } else {
341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
342 }
343 }
344 }
345 } 127 }
346
347 return null;
348 } 128 }
349 129
350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { 130 protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) {
351 131 MethodEntry referencedMethod = new MethodEntry(entryPool.getClass(owner), name, new MethodDescriptor(desc));
352 // clearly this would be silly 132 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod);
353 if (outerClassEntry.equals(innerClassEntry)) { 133 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
354 return false; 134 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
355 } 135 }
356 136 methodReferences.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
357 // is the outer class in the jar?
358 return this.obfClassEntries.contains(outerClassEntry);
359
360 } 137 }
361 138
362 @SuppressWarnings("unchecked") 139 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { 140 FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc));
364 141 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField);
365 // illegal constructors only set synthetic member fields, then call super() 142 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) {
366 String className = constructor.getDeclaringClass().getName(); 143 referencedField = referencedField.updateOwnership(resolvedClassEntry);
367
368 // collect all the field accesses, constructor calls, and method calls
369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
370 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
371 try {
372 constructor.instrument(new ExprEditor() {
373 @Override
374 public void edit(FieldAccess fieldAccess) {
375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
376 illegalFieldWrites.add(fieldAccess);
377 }
378 }
379
380 @Override
381 public void edit(ConstructorCall constructorCall) {
382 constructorCalls.add(constructorCall);
383 }
384 });
385 } catch (CannotCompileException ex) {
386 // we're not compiling anything... this is stupid
387 throw new Error(ex);
388 }
389
390 // are there any illegal field writes?
391 if (illegalFieldWrites.isEmpty()) {
392 return false;
393 }
394
395 // are all the writes to synthetic fields?
396 for (FieldAccess fieldWrite : illegalFieldWrites) {
397
398 // all illegal writes have to be to the local class
399 if (!fieldWrite.getClassName().equals(className)) {
400 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
401 return false;
402 }
403
404 // find the field
405 FieldInfo fieldInfo = null;
406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) {
407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
408 fieldInfo = info;
409 break;
410 }
411 }
412 if (fieldInfo == null) {
413 // field is in a superclass or something, can't be a local synthetic member
414 return false;
415 }
416
417 // is this field synthetic?
418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
419 if (isSynthetic) {
420 syntheticFieldTypes.add(fieldInfo.getDescriptor());
421 } else {
422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
423 return false;
424 }
425 } 144 }
426 145 fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry));
427 // we passed all the tests!
428 return true;
429 } 146 }
430 147
431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { 148 public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) {
432 149 this.innerClassesByOuter.put(outerEntry, innerEntry);
433 // is this class already marked anonymous? 150 boolean innerWasAdded = this.outerClassesByInner.put(innerEntry, outerEntry) == null;
434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 151 assert (innerWasAdded);
435 if (enclosingMethodAttribute != null) { 152 }
436 if (enclosingMethodAttribute.methodIndex() > 0) {
437 return EntryFactory.getBehaviorEntry(
438 Descriptor.toJvmName(enclosingMethodAttribute.className()),
439 enclosingMethodAttribute.methodName(),
440 enclosingMethodAttribute.methodDescriptor()
441 );
442 } else {
443 // an attribute but no method? assume not anonymous
444 return null;
445 }
446 }
447
448 // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous
449 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
450 if (innerClassesAttribute != null) {
451 return null;
452 }
453 153
454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 154 private MethodEntry findBridgedMethod(MethodDefEntry method) {
455 155
456 // anonymous classes: 156 // bridge methods just call another method, cast it to the return desc, and return the result
457 // can't be abstract 157 // let's see if we can detect this scenario
458 // have only one constructor
459 // it's called exactly once by the outer class
460 // the type the instance is assigned to can't be this type
461 158
462 // is abstract? 159 // skip non-synthetic methods
463 if (Modifier.isAbstract(c.getModifiers())) { 160 if (!method.getAccess().isSynthetic()) {
464 return null; 161 return null;
465 } 162 }
466 163
467 // is there exactly one constructor? 164 // get all the called methods
468 if (c.getDeclaredConstructors().length != 1) { 165 final Collection<EntryReference<MethodEntry, MethodDefEntry>> referencedMethods = methodReferences.get(method);
469 return null;
470 }
471 CtConstructor constructor = c.getDeclaredConstructors()[0];
472 166
473 // is this constructor called exactly once? 167 // is there just one?
474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 168 if (referencedMethods.size() != 1) {
475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
476 if (references.size() != 1) {
477 return null; 169 return null;
478 } 170 }
479 171
480 // does the caller use this type? 172 // we have a bridge method!
481 BehaviorEntry caller = references.iterator().next().context; 173 return referencedMethods.stream().findFirst().get().entry;
482 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) {
484 // caller references this type, so it can't be anonymous
485 return null;
486 }
487 }
488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) {
490 return null;
491 }
492 }
493
494 return caller;
495 } 174 }
496 175
497 public Set<ClassEntry> getObfClassEntries() { 176 public Set<ClassEntry> getObfClassEntries() {
498 return this.obfClassEntries; 177 return this.obfClassEntries;
499 } 178 }
500 179
501 public Collection<FieldEntry> getObfFieldEntries() { 180 public Collection<FieldDefEntry> getObfFieldEntries() {
502 return this.fields.values(); 181 return this.fields.values();
503 } 182 }
504 183
505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { 184 public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) {
506 return this.fields.get(classEntry); 185 return this.fields.get(classEntry);
507 } 186 }
508 187
509 public Collection<BehaviorEntry> getObfBehaviorEntries() { 188 public Collection<MethodDefEntry> getObfBehaviorEntries() {
510 return this.behaviors.values(); 189 return this.methods.values();
511 } 190 }
512 191
513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { 192 public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) {
514 return this.behaviors.get(classEntry); 193 return this.methods.get(classEntry);
515 } 194 }
516 195
517 public TranslationIndex getTranslationIndex() { 196 public TranslationIndex getTranslationIndex() {
@@ -533,8 +212,8 @@ public class JarIndex {
533 } 212 }
534 } 213 }
535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 214 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
536 deobfuscatingTranslator, 215 deobfuscatingTranslator,
537 ancestry.get(ancestry.size() - 1) 216 ancestry.get(ancestry.size() - 1)
538 ); 217 );
539 218
540 // expand all children recursively 219 // expand all children recursively
@@ -557,28 +236,20 @@ public class JarIndex {
557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 236 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
558 237
559 // travel to the ancestor implementation 238 // travel to the ancestor implementation
560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); 239 ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry();
561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { 240 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getOwnerClassEntry())) {
562 MethodEntry ancestorMethodEntry = new MethodEntry( 241 MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
563 new ClassEntry(ancestorClassEntry), 242 if (ancestorMethodEntry != null && containsObfMethod(ancestorMethodEntry)) {
564 obfMethodEntry.getName(),
565 obfMethodEntry.getSignature()
566 );
567 if (containsObfBehavior(ancestorMethodEntry)) {
568 baseImplementationClassEntry = ancestorClassEntry; 243 baseImplementationClassEntry = ancestorClassEntry;
569 } 244 }
570 } 245 }
571 246
572 // make a root node at the base 247 // make a root node at the base
573 MethodEntry methodEntry = new MethodEntry( 248 MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
574 baseImplementationClassEntry,
575 obfMethodEntry.getName(),
576 obfMethodEntry.getSignature()
577 );
578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 249 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
579 deobfuscatingTranslator, 250 deobfuscatingTranslator,
580 methodEntry, 251 methodEntry,
581 containsObfBehavior(methodEntry) 252 containsObfMethod(methodEntry)
582 ); 253 );
583 254
584 // expand the full tree 255 // expand the full tree
@@ -599,12 +270,8 @@ public class JarIndex {
599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { 270 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
600 271
601 // is this method defined in this interface? 272 // is this method defined in this interface?
602 MethodEntry methodInterface = new MethodEntry( 273 MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
603 interfaceEntry, 274 if (methodInterface != null && containsObfMethod(methodInterface)) {
604 obfMethodEntry.getName(),
605 obfMethodEntry.getSignature()
606 );
607 if (containsObfBehavior(methodInterface)) {
608 interfaceMethodEntries.add(methodInterface); 275 interfaceMethodEntries.add(methodInterface);
609 } 276 }
610 } 277 }
@@ -623,14 +290,14 @@ public class JarIndex {
623 290
624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { 291 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
625 Set<MethodEntry> methodEntries = Sets.newHashSet(); 292 Set<MethodEntry> methodEntries = Sets.newHashSet();
626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); 293 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry));
627 return methodEntries; 294 return methodEntries;
628 } 295 }
629 296
630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 297 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
631 MethodEntry methodEntry = node.getMethodEntry(); 298 MethodEntry methodEntry = node.getMethodEntry();
632 299
633 if (containsObfBehavior(methodEntry)) { 300 if (containsObfMethod(methodEntry)) {
634 // collect the entry 301 // collect the entry
635 methodEntries.add(methodEntry); 302 methodEntries.add(methodEntry);
636 } 303 }
@@ -643,7 +310,7 @@ public class JarIndex {
643 } 310 }
644 311
645 // look at interface methods too 312 // look at interface methods too
646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { 313 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) {
647 getRelatedMethodImplementations(methodEntries, implementationsNode); 314 getRelatedMethodImplementations(methodEntries, implementationsNode);
648 } 315 }
649 316
@@ -655,7 +322,7 @@ public class JarIndex {
655 322
656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 323 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
657 MethodEntry methodEntry = node.getMethodEntry(); 324 MethodEntry methodEntry = node.getMethodEntry();
658 if (containsObfBehavior(methodEntry)) { 325 if (containsObfMethod(methodEntry)) {
659 // collect the entry 326 // collect the entry
660 methodEntries.add(methodEntry); 327 methodEntries.add(methodEntry);
661 } 328 }
@@ -673,30 +340,30 @@ public class JarIndex {
673 } 340 }
674 } 341 }
675 342
676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { 343 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) {
677 return this.fieldReferences.get(fieldEntry); 344 return this.fieldReferences.get(fieldEntry);
678 } 345 }
679 346
680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { 347 public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) {
681 // linear search is fast enough for now 348 // linear search is fast enough for now
682 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 349 Set<FieldEntry> fieldEntries = Sets.newHashSet();
683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { 350 for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) {
684 if (reference.context == behaviorEntry) { 351 if (reference.context == methodEntry) {
685 fieldEntries.add(reference.entry); 352 fieldEntries.add(reference.entry);
686 } 353 }
687 } 354 }
688 return fieldEntries; 355 return fieldEntries;
689 } 356 }
690 357
691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { 358 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodReferences(MethodEntry methodEntry) {
692 return this.behaviorReferences.get(behaviorEntry); 359 return this.methodReferences.get(methodEntry);
693 } 360 }
694 361
695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { 362 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
696 // linear search is fast enough for now 363 // linear search is fast enough for now
697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); 364 Set<MethodEntry> behaviorEntries = Sets.newHashSet();
698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { 365 for (EntryReference<MethodEntry, MethodDefEntry> reference : this.methodReferences.values()) {
699 if (reference.context == behaviorEntry) { 366 if (reference.context == methodEntry) {
700 behaviorEntries.add(reference.entry); 367 behaviorEntries.add(reference.entry);
701 } 368 }
702 } 369 }
@@ -711,20 +378,12 @@ public class JarIndex {
711 return this.outerClassesByInner.get(obfInnerClassEntry); 378 return this.outerClassesByInner.get(obfInnerClassEntry);
712 } 379 }
713 380
714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) {
715 return this.anonymousClasses.containsKey(obfInnerClassEntry);
716 }
717
718 public boolean isSyntheticMethod(MethodEntry methodEntry) { 381 public boolean isSyntheticMethod(MethodEntry methodEntry) {
719 return this.syntheticMethods.contains(methodEntry); 382 return this.syntheticMethods.contains(methodEntry);
720 } 383 }
721 384
722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) {
723 return this.anonymousClasses.get(obfInnerClassName);
724 }
725
726 public Set<ClassEntry> getInterfaces(String className) { 385 public Set<ClassEntry> getInterfaces(String className) {
727 ClassEntry classEntry = new ClassEntry(className); 386 ClassEntry classEntry = entryPool.getClass(className);
728 Set<ClassEntry> interfaces = new HashSet<>(); 387 Set<ClassEntry> interfaces = new HashSet<>();
729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); 388 interfaces.addAll(this.translationIndex.getInterfaces(classEntry));
730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { 389 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
@@ -754,7 +413,7 @@ public class JarIndex {
754 } 413 }
755 414
756 public boolean isInterface(String className) { 415 public boolean isInterface(String className) {
757 return this.translationIndex.isInterface(new ClassEntry(className)); 416 return this.translationIndex.isInterface(entryPool.getClass(className));
758 } 417 }
759 418
760 public boolean containsObfClass(ClassEntry obfClassEntry) { 419 public boolean containsObfClass(ClassEntry obfClassEntry) {
@@ -765,8 +424,8 @@ public class JarIndex {
765 return this.access.containsKey(obfFieldEntry); 424 return this.access.containsKey(obfFieldEntry);
766 } 425 }
767 426
768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { 427 public boolean containsObfMethod(MethodEntry obfMethodEntry) {
769 return this.access.containsKey(obfBehaviorEntry); 428 return this.access.containsKey(obfMethodEntry);
770 } 429 }
771 430
772 public boolean containsEntryWithSameName(Entry entry) { 431 public boolean containsEntryWithSameName(Entry entry) {
@@ -776,15 +435,13 @@ public class JarIndex {
776 return false; 435 return false;
777 } 436 }
778 437
779 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 438 public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) {
780 // check the behavior 439 // check the behavior
781 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 440 if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) {
782 return false; 441 return false;
783 } 442 }
784 443
785 // check the argument 444 return true;
786 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size();
787
788 } 445 }
789 446
790 public boolean containsObfEntry(Entry obfEntry) { 447 public boolean containsObfEntry(Entry obfEntry) {
@@ -792,15 +449,12 @@ public class JarIndex {
792 return containsObfClass((ClassEntry) obfEntry); 449 return containsObfClass((ClassEntry) obfEntry);
793 } else if (obfEntry instanceof FieldEntry) { 450 } else if (obfEntry instanceof FieldEntry) {
794 return containsObfField((FieldEntry) obfEntry); 451 return containsObfField((FieldEntry) obfEntry);
795 } else if (obfEntry instanceof BehaviorEntry) { 452 } else if (obfEntry instanceof MethodEntry) {
796 return containsObfBehavior((BehaviorEntry) obfEntry); 453 return containsObfMethod((MethodEntry) obfEntry);
797 } else if (obfEntry instanceof ArgumentEntry) {
798 return containsObfArgument((ArgumentEntry) obfEntry);
799 } else if (obfEntry instanceof LocalVariableEntry) { 454 } else if (obfEntry instanceof LocalVariableEntry) {
800 // TODO: Implement it 455 return containsObfVariable((LocalVariableEntry) obfEntry);
801 return false;
802 } else { 456 } else {
803 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 457 throw new Error("Entry desc not supported: " + obfEntry.getClass().getName());
804 } 458 }
805 } 459 }
806 460
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index bacb1aa..6791b83 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -54,11 +54,11 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
54 } 54 }
55 55
56 public String getDeobfClassName() { 56 public String getDeobfClassName() {
57 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 57 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName();
58 } 58 }
59 59
60 public String getDeobfMethodName() { 60 public String getDeobfMethodName() {
61 return this.deobfuscatingTranslator.translate(this.entry); 61 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 } 62 }
63 63
64 @Override 64 @Override
@@ -80,9 +80,8 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
80 // get all method implementations 80 // get all method implementations
81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() 83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc());
84 ); 84 if (index.containsObfMethod(methodEntry)) {
85 if (index.containsObfBehavior(methodEntry)) {
86 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 85 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry));
87 } 86 }
88 } 87 }
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index 4f84dd0..f060ed9 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -52,11 +52,11 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
52 } 52 }
53 53
54 public String getDeobfClassName() { 54 public String getDeobfClassName() {
55 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 55 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName();
56 } 56 }
57 57
58 public String getDeobfMethodName() { 58 public String getDeobfMethodName() {
59 return this.deobfuscatingTranslator.translate(this.entry); 59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
60 } 60 }
61 61
62 public boolean isImplemented() { 62 public boolean isImplemented() {
@@ -84,11 +84,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
84 public void load(JarIndex index, boolean recurse) { 84 public void load(JarIndex index, boolean recurse) {
85 // get all the child nodes 85 // get all the child nodes
86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { 87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) {
88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() 88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc());
89 ); 89 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry)));
90 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry)
91 ));
92 } 90 }
93 91
94 // add them to this node 92 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index 6556b2c..37b4073 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -12,30 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.mapping.BehaviorEntry; 15import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.Entry;
17import cuchaz.enigma.mapping.Translator;
18 16
19import javax.swing.tree.DefaultMutableTreeNode; 17import javax.swing.tree.DefaultMutableTreeNode;
20import javax.swing.tree.TreeNode; 18import javax.swing.tree.TreeNode;
21import java.util.Set; 19import java.util.Set;
22 20
23public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode 21public class MethodReferenceTreeNode extends DefaultMutableTreeNode
24 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> { 22 implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
25 23
26 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
27 private BehaviorEntry entry; 25 private MethodEntry entry;
28 private EntryReference<BehaviorEntry, BehaviorEntry> reference; 26 private EntryReference<MethodEntry, MethodDefEntry> reference;
29 private Access access; 27 private Access access;
30 28
31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { 29 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) {
32 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.entry = entry; 31 this.entry = entry;
34 this.reference = null; 32 this.reference = null;
35 } 33 }
36 34
37 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, 35 public MethodReferenceTreeNode(Translator deobfuscatingTranslator,
38 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) { 36 EntryReference<MethodEntry, MethodDefEntry> reference, Access access) {
39 this.deobfuscatingTranslator = deobfuscatingTranslator; 37 this.deobfuscatingTranslator = deobfuscatingTranslator;
40 this.entry = reference.entry; 38 this.entry = reference.entry;
41 this.reference = reference; 39 this.reference = reference;
@@ -43,42 +41,42 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode
43 } 41 }
44 42
45 @Override 43 @Override
46 public BehaviorEntry getEntry() { 44 public MethodEntry getEntry() {
47 return this.entry; 45 return this.entry;
48 } 46 }
49 47
50 @Override 48 @Override
51 public EntryReference<BehaviorEntry, BehaviorEntry> getReference() { 49 public EntryReference<MethodEntry, MethodDefEntry> getReference() {
52 return this.reference; 50 return this.reference;
53 } 51 }
54 52
55 @Override 53 @Override
56 public String toString() { 54 public String toString() {
57 if (this.reference != null) { 55 if (this.reference != null) {
58 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), 56 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context),
59 this.access); 57 this.access);
60 } 58 }
61 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 } 60 }
63 61
64 public void load(JarIndex index, boolean recurse) { 62 public void load(JarIndex index, boolean recurse) {
65 // get all the child nodes 63 // get all the child nodes
66 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) { 64 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.entry)) {
67 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 65 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
68 } 66 }
69 67
70 if (recurse && this.children != null) { 68 if (recurse && this.children != null) {
71 for (Object child : this.children) { 69 for (Object child : this.children) {
72 if (child instanceof BehaviorReferenceTreeNode) { 70 if (child instanceof MethodReferenceTreeNode) {
73 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; 71 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child;
74 72
75 // don't recurse into ancestor 73 // don't recurse into ancestor
76 Set<Entry> ancestors = Sets.newHashSet(); 74 Set<Entry> ancestors = Sets.newHashSet();
77 TreeNode n = node; 75 TreeNode n = node;
78 while (n.getParent() != null) { 76 while (n.getParent() != null) {
79 n = n.getParent(); 77 n = n.getParent();
80 if (n instanceof BehaviorReferenceTreeNode) { 78 if (n instanceof MethodReferenceTreeNode) {
81 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); 79 ancestors.add(((MethodReferenceTreeNode) n).getEntry());
82 } 80 }
83 } 81 }
84 if (ancestors.contains(node.getEntry())) { 82 if (ancestors.contains(node.getEntry())) {
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
new file mode 100644
index 0000000..78ef722
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -0,0 +1,72 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.mapping.ClassEntry;
15import org.objectweb.asm.ClassReader;
16import org.objectweb.asm.tree.ClassNode;
17
18import java.io.IOException;
19import java.io.InputStream;
20import java.util.*;
21import java.util.function.Consumer;
22import java.util.jar.JarEntry;
23import java.util.jar.JarFile;
24
25public class ParsedJar {
26 private final Map<String, ClassNode> nodes = new LinkedHashMap<>();
27
28 public ParsedJar(JarFile jar) throws IOException {
29 try {
30 // get the jar entries that correspond to classes
31 Enumeration<JarEntry> entries = jar.entries();
32 while (entries.hasMoreElements()) {
33 JarEntry entry = entries.nextElement();
34 // is this a class file?
35 if (entry.getName().endsWith(".class")) {
36 try (InputStream input = jar.getInputStream(entry)) {
37 // read the ClassNode from the jar
38 ClassReader reader = new ClassReader(input);
39 ClassNode node = new ClassNode();
40 reader.accept(node, 0);
41 String path = entry.getName().substring(0, entry.getName().length() - ".class".length());
42 nodes.put(path, node);
43 }
44 }
45 }
46 } finally {
47 jar.close();
48 }
49 }
50
51 public void visit(Consumer<ClassNode> visitor) {
52 for (ClassNode node : nodes.values()) {
53 visitor.accept(node);
54 }
55 }
56
57 public int getClassCount() {
58 return nodes.size();
59 }
60
61 public List<ClassEntry> getClassEntries() {
62 List<ClassEntry> entries = new ArrayList<>(nodes.size());
63 for (ClassNode node : nodes.values()) {
64 entries.add(new ClassEntry(node.name));
65 }
66 return entries;
67 }
68
69 public ClassNode getClassNode(String name) {
70 return nodes.get(name);
71 }
72}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index b13415d..015eaac 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -20,11 +20,16 @@ import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.mapping.*; 20import cuchaz.enigma.mapping.*;
21 21
22public class SourceIndexClassVisitor extends SourceIndexVisitor { 22public class SourceIndexClassVisitor extends SourceIndexVisitor {
23 private final ReferencedEntryPool entryPool;
24 private final ProcyonEntryFactory entryFactory;
23 25
24 private ClassEntry classEntry; 26 private ClassEntry classEntry;
25 private boolean isEnum; 27 private boolean isEnum;
26 28
27 public SourceIndexClassVisitor(ClassEntry classEntry) { 29 public SourceIndexClassVisitor(ReferencedEntryPool entryPool, ClassEntry classEntry) {
30 super(entryPool);
31 this.entryPool = entryPool;
32 this.entryFactory = new ProcyonEntryFactory(entryPool);
28 this.classEntry = classEntry; 33 this.classEntry = classEntry;
29 } 34 }
30 35
@@ -34,9 +39,9 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 39 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
35 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 40 ClassEntry classEntry = new ClassEntry(def.getInternalName());
36 if (!classEntry.equals(this.classEntry)) { 41 if (!classEntry.equals(this.classEntry)) {
37 // it's a sub-type, recurse 42 // it's a subtype, recurse
38 index.addDeclaration(node.getNameToken(), classEntry); 43 index.addDeclaration(node.getNameToken(), classEntry);
39 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 44 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
40 } 45 }
41 46
42 return recurse(node, index); 47 return recurse(node, index);
@@ -56,31 +61,28 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
56 @Override 61 @Override
57 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 62 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 63 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
59 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); 64 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
60 AstNode tokenNode = node.getNameToken(); 65 AstNode tokenNode = node.getNameToken();
61 if (behaviorEntry instanceof ConstructorEntry) { 66 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
62 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; 67 // for static initializers, check elsewhere for the token node
63 if (constructorEntry.isStatic()) { 68 tokenNode = node.getModifiers().firstOrNullObject();
64 // for static initializers, check elsewhere for the token node
65 tokenNode = node.getModifiers().firstOrNullObject();
66 }
67 } 69 }
68 index.addDeclaration(tokenNode, behaviorEntry); 70 index.addDeclaration(tokenNode, methodEntry);
69 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry, false), index); 71 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry, false), index);
70 } 72 }
71 73
72 @Override 74 @Override
73 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 75 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
74 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 76 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
75 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); 77 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
76 index.addDeclaration(node.getNameToken(), constructorEntry); 78 index.addDeclaration(node.getNameToken(), methodEntry);
77 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry, isEnum), index); 79 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry, isEnum), index);
78 } 80 }
79 81
80 @Override 82 @Override
81 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 83 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
82 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 84 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
83 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 85 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
84 assert (node.getVariables().size() == 1); 86 assert (node.getVariables().size() == 1);
85 VariableInitializer variable = node.getVariables().firstOrNullObject(); 87 VariableInitializer variable = node.getVariables().firstOrNullObject();
86 index.addDeclaration(variable.getNameToken(), fieldEntry); 88 index.addDeclaration(variable.getNameToken(), fieldEntry);
@@ -92,7 +94,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
92 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 94 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
93 // treat enum declarations as field declarations 95 // treat enum declarations as field declarations
94 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 96 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
95 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 97 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
96 index.addDeclaration(node.getNameToken(), fieldEntry); 98 index.addDeclaration(node.getNameToken(), fieldEntry);
97 this.isEnum = true; 99 this.isEnum = true;
98 return recurse(node, index); 100 return recurse(node, index);
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 1b61916..2a62241 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -20,13 +20,15 @@ import com.strobel.assembler.metadata.TypeReference;
20import com.strobel.decompiler.languages.TextLocation; 20import com.strobel.decompiler.languages.TextLocation;
21import com.strobel.decompiler.languages.java.ast.*; 21import com.strobel.decompiler.languages.java.ast.*;
22import cuchaz.enigma.mapping.*; 22import cuchaz.enigma.mapping.*;
23import javassist.bytecode.Descriptor;
24 23
25import java.util.HashMap; 24import java.util.HashMap;
26import java.util.Map; 25import java.util.Map;
27 26
28public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 27public class SourceIndexMethodVisitor extends SourceIndexVisitor {
29 private BehaviorEntry behaviorEntry; 28 private final ReferencedEntryPool entryPool;
29 private final ProcyonEntryFactory entryFactory;
30
31 private MethodDefEntry methodEntry;
30 32
31 // TODO: Really fix Procyon index problem with inner classes 33 // TODO: Really fix Procyon index problem with inner classes
32 private int argumentPosition; 34 private int argumentPosition;
@@ -34,8 +36,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 36 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 37 private Map<String, Entry> identifierEntryCache = new HashMap<>();
36 38
37 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry, boolean isEnum) { 39 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry, boolean isEnum) {
38 this.behaviorEntry = behaviorEntry; 40 super(entryPool);
41 this.entryPool = entryPool;
42 this.entryFactory = new ProcyonEntryFactory(entryPool);
43 this.methodEntry = methodEntry;
39 this.argumentPosition = isEnum ? 2 : 0; 44 this.argumentPosition = isEnum ? 2 : 0;
40 this.localsPosition = 0; 45 this.localsPosition = 0;
41 } 46 }
@@ -45,19 +50,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
45 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 50 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
46 51
47 // get the behavior entry 52 // get the behavior entry
48 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 53 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
49 BehaviorEntry behaviorEntry = null; 54 MethodEntry methodEntry = null;
50 if (ref instanceof MethodReference) { 55 if (ref instanceof MethodReference) {
51 MethodReference methodRef = (MethodReference) ref; 56 methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature());
52 if (methodRef.isConstructor()) {
53 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
54 } else if (methodRef.isTypeInitializer()) {
55 behaviorEntry = new ConstructorEntry(classEntry);
56 } else {
57 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature()));
58 }
59 } 57 }
60 if (behaviorEntry != null) { 58 if (methodEntry != null) {
61 // get the node for the token 59 // get the node for the token
62 AstNode tokenNode = null; 60 AstNode tokenNode = null;
63 if (node.getTarget() instanceof MemberReferenceExpression) { 61 if (node.getTarget() instanceof MemberReferenceExpression) {
@@ -68,13 +66,13 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
68 tokenNode = node.getTarget(); 66 tokenNode = node.getTarget();
69 } 67 }
70 if (tokenNode != null) { 68 if (tokenNode != null) {
71 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); 69 index.addReference(tokenNode, methodEntry, this.methodEntry);
72 } 70 }
73 } 71 }
74 72
75 // Check for identifier 73 // Check for identifier
76 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 74 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
77 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); 75 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
78 return recurse(node, index); 76 return recurse(node, index);
79 } 77 }
80 78
@@ -83,13 +81,17 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
83 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 81 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
84 if (ref != null) { 82 if (ref != null) {
85 // make sure this is actually a field 83 // make sure this is actually a field
86 if (ref.getErasedSignature().indexOf('(') >= 0) { 84 String erasedSignature = ref.getErasedSignature();
85 if (erasedSignature.indexOf('(') >= 0) {
87 throw new Error("Expected a field here! got " + ref); 86 throw new Error("Expected a field here! got " + ref);
88 } 87 }
89 88
90 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 89 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
91 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 90 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
92 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); 91 if (fieldEntry == null) {
92 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName());
93 }
94 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
93 } 95 }
94 96
95 return recurse(node, index); 97 return recurse(node, index);
@@ -99,8 +101,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
99 public Void visitSimpleType(SimpleType node, SourceIndex index) { 101 public Void visitSimpleType(SimpleType node, SourceIndex index) {
100 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 102 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
101 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 103 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
102 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 104 ClassEntry classEntry = entryPool.getClass(ref.getInternalName());
103 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); 105 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry);
104 } 106 }
105 107
106 return recurse(node, index); 108 return recurse(node, index);
@@ -110,12 +112,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
110 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 112 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
111 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 113 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
112 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) { 114 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) {
113 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), 115 MethodEntry methodEntry = entryFactory.getMethodEntry((MethodReference) def.getMethod());
114 argumentPosition++, node.getName()); 116 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition++, node.getName());
115 Identifier identifier = node.getNameToken(); 117 Identifier identifier = node.getNameToken();
116 // cache the argument entry and the identifier 118 // cache the argument entry and the identifier
117 identifierEntryCache.put(identifier.getName(), argumentEntry); 119 identifierEntryCache.put(identifier.getName(), localVariableEntry);
118 index.addDeclaration(identifier, argumentEntry); 120 index.addDeclaration(identifier, localVariableEntry);
119 } 121 }
120 122
121 return recurse(node, index); 123 return recurse(node, index);
@@ -125,9 +127,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
125 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 127 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
126 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 128 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
127 if (ref != null) { 129 if (ref != null) {
128 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 130 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
129 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 131 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
130 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); 132 if (fieldEntry == null) {
133 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName());
134 }
135 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
131 } else 136 } else
132 this.checkIdentifier(node, index); 137 this.checkIdentifier(node, index);
133 return recurse(node, index); 138 return recurse(node, index);
@@ -155,11 +160,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
155 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 160 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 161 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
157 if (ref != null) { 162 if (ref != null) {
158 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 163 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
159 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 164 MethodEntry constructorEntry = entryPool.getMethod(classEntry, "<init>", ref.getErasedSignature());
160 if (node.getType() instanceof SimpleType) { 165 if (node.getType() instanceof SimpleType) {
161 SimpleType simpleTypeNode = (SimpleType) node.getType(); 166 SimpleType simpleTypeNode = (SimpleType) node.getType();
162 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); 167 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry);
163 } 168 }
164 } 169 }
165 170
@@ -169,11 +174,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
169 @Override 174 @Override
170 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 175 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
171 if (node.getVariableType() instanceof SimpleType) { 176 if (node.getVariableType() instanceof SimpleType) {
172 SimpleType type = (SimpleType) node.getVariableType();
173 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
174 Identifier identifier = node.getVariableNameToken(); 177 Identifier identifier = node.getVariableNameToken();
175 String signature = Descriptor.of(typeReference.getErasedDescription()); 178 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition + localsPosition++, identifier.getName());
176 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, identifier.getName(), new Type(signature));
177 identifierEntryCache.put(identifier.getName(), localVariableEntry); 179 identifierEntryCache.put(identifier.getName(), localVariableEntry);
178 addDeclarationToUnmatched(identifier.getName(), index); 180 addDeclarationToUnmatched(identifier.getName(), index);
179 index.addDeclaration(identifier, localVariableEntry); 181 index.addDeclaration(identifier, localVariableEntry);
@@ -189,11 +191,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
189 if (variables.size() == 1) { 191 if (variables.size() == 1) {
190 VariableInitializer initializer = variables.firstOrNullObject(); 192 VariableInitializer initializer = variables.firstOrNullObject();
191 if (initializer != null && node.getType() instanceof SimpleType) { 193 if (initializer != null && node.getType() instanceof SimpleType) {
192 SimpleType type = (SimpleType) node.getType();
193 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
194 String signature = Descriptor.of(typeReference.getErasedDescription());
195 Identifier identifier = initializer.getNameToken(); 194 Identifier identifier = initializer.getNameToken();
196 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, initializer.getName(), new Type(signature)); 195 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition + localsPosition++, initializer.getName());
197 identifierEntryCache.put(identifier.getName(), localVariableEntry); 196 identifierEntryCache.put(identifier.getName(), localVariableEntry);
198 addDeclarationToUnmatched(identifier.getName(), index); 197 addDeclarationToUnmatched(identifier.getName(), index);
199 index.addDeclaration(identifier, localVariableEntry); 198 index.addDeclaration(identifier, localVariableEntry);
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index a94a55b..241b9f7 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -15,8 +15,14 @@ import com.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.*; 15import com.strobel.decompiler.languages.java.ast.*;
16import com.strobel.decompiler.patterns.Pattern; 16import com.strobel.decompiler.patterns.Pattern;
17import cuchaz.enigma.mapping.ClassEntry; 17import cuchaz.enigma.mapping.ClassEntry;
18import cuchaz.enigma.mapping.ReferencedEntryPool;
18 19
19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 20public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
21 private final ReferencedEntryPool entryPool;
22
23 public SourceIndexVisitor(ReferencedEntryPool entryPool) {
24 this.entryPool = entryPool;
25 }
20 26
21 @Override 27 @Override
22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 28 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
@@ -24,7 +30,7 @@ public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
24 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 30 ClassEntry classEntry = new ClassEntry(def.getInternalName());
25 index.addDeclaration(node.getNameToken(), classEntry); 31 index.addDeclaration(node.getNameToken(), classEntry);
26 32
27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 33 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
28 } 34 }
29 35
30 protected Void recurse(AstNode node, SourceIndex index) { 36 protected Void recurse(AstNode node, SourceIndex index) {
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index 26be05b..3cd80ff 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -15,11 +15,8 @@ import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
18import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.*; 19import cuchaz.enigma.mapping.*;
19import javassist.CtBehavior;
20import javassist.CtClass;
21import javassist.CtField;
22import javassist.bytecode.Descriptor;
23 20
24import java.util.Collection; 21import java.util.Collection;
25import java.util.List; 22import java.util.List;
@@ -28,96 +25,91 @@ import java.util.Set;
28 25
29public class TranslationIndex { 26public class TranslationIndex {
30 27
28 private final ReferencedEntryPool entryPool;
31 private Map<ClassEntry, ClassEntry> superclasses; 29 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 30 private Multimap<ClassEntry, FieldDefEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 31 private Multimap<ClassEntry, MethodDefEntry> methodEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 32 private Multimap<ClassEntry, ClassEntry> interfaces;
35 33
36 public TranslationIndex() { 34 public TranslationIndex(ReferencedEntryPool entryPool) {
35 this.entryPool = entryPool;
37 this.superclasses = Maps.newHashMap(); 36 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 37 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 38 this.methodEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 39 this.interfaces = HashMultimap.create();
41 } 40 }
42 41
43 public TranslationIndex(TranslationIndex other, Translator translator) { 42 public TranslationIndex(TranslationIndex other, Translator translator) {
43 this.entryPool = other.entryPool;
44
44 // translate the superclasses 45 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 46 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 47 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 48 this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue()));
48 } 49 }
49 50
50 // translate the interfaces 51 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 52 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 53 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 54 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 55 translator.getTranslatedClass(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 56 translator.getTranslatedClass(mapEntry.getValue())
56 ); 57 );
57 } 58 }
58 59
59 // translate the fields 60 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 61 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 62 for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 63 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 64 translator.getTranslatedClass(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 65 translator.getTranslatedFieldDef(mapEntry.getValue())
65 ); 66 );
66 } 67 }
67 68
68 this.behaviorEntries = HashMultimap.create(); 69 this.methodEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 70 for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) {
70 this.behaviorEntries.put( 71 this.methodEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 72 translator.getTranslatedClass(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 73 translator.getTranslatedMethodDef(mapEntry.getValue())
73 ); 74 );
74 } 75 }
75 } 76 }
76 77
77 public void indexClass(CtClass c) { 78 protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) {
78 indexClass(c, true); 79 ClassDefEntry classEntry = new ClassDefEntry(name, new AccessFlags(access));
79 }
80
81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 80 if (isJre(classEntry)) {
84 return; 81 return null;
85 } 82 }
86 83
87 // add the superclass 84 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 85 ClassEntry superclassEntry = entryPool.getClass(superName);
89 if (superclassEntry != null) { 86 if (!isJre(superclassEntry)) {
90 this.superclasses.put(classEntry, superclassEntry); 87 this.superclasses.put(classEntry, superclassEntry);
91 } 88 }
92 89
93 // add the interfaces 90 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 91 for (String interfaceClassName : interfaces) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 92 ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName);
96 if (!isJre(interfaceClassEntry)) { 93 if (!isJre(interfaceClassEntry)) {
97
98 this.interfaces.put(classEntry, interfaceClassEntry); 94 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 95 }
100 } 96 }
101 97
102 if (indexMembers) { 98 return classEntry;
103 // add fields 99 }
104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 }
108 100
109 // add behaviors 101 protected void indexField(FieldDefEntry fieldEntry) {
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 102 this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 103 }
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 104
113 } 105 protected void indexMethod(MethodDefEntry methodEntry) {
114 } 106 this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry);
115 } 107 }
116 108
117 public void renameClasses(Map<String, String> renames) { 109 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 110 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 111 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 112 EntryRenamer.renameClassesInMultimap(renames, this.methodEntries);
121 } 113 }
122 114
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 115 public ClassEntry getSuperclass(ClassEntry classEntry) {
@@ -175,31 +167,32 @@ public class TranslationIndex {
175 } 167 }
176 168
177 public boolean entryExists(Entry entry) { 169 public boolean entryExists(Entry entry) {
170 if (entry == null) {
171 return false;
172 }
178 if (entry instanceof FieldEntry) { 173 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 174 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 175 } else if (entry instanceof MethodEntry) {
181 return behaviorExists((BehaviorEntry) entry); 176 return methodExists((MethodEntry) entry);
182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 177 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 178 return methodExists(((LocalVariableEntry) entry).getOwnerEntry());
186 } 179 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 180 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 181 }
189 182
190 public boolean fieldExists(FieldEntry fieldEntry) { 183 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 184 return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry);
192 } 185 }
193 186
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 187 public boolean methodExists(MethodEntry methodEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 188 return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry);
196 } 189 }
197 190
198 public ClassEntry resolveEntryClass(Entry entry) { 191 public ClassEntry resolveEntryOwner(Entry entry) {
199 return resolveEntryClass(entry, false); 192 return resolveEntryOwner(entry, false);
200 } 193 }
201 194
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 195 public ClassEntry resolveEntryOwner(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 196 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 197 return (ClassEntry) entry;
205 } 198 }
@@ -227,12 +220,12 @@ public class TranslationIndex {
227 Entry originalEntry = entry; 220 Entry originalEntry = entry;
228 221
229 // Get all possible superclasses and reverse the list 222 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 223 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getOwnerClassEntry()));
231 224
232 boolean existInEntry = false; 225 boolean existInEntry = false;
233 226
234 for (ClassEntry classEntry : superclasses) { 227 for (ClassEntry classEntry : superclasses) {
235 entry = entry.cloneToNewClass(classEntry); 228 entry = entry.updateOwnership(classEntry);
236 existInEntry = entryExists(entry); 229 existInEntry = entryExists(entry);
237 230
238 // Check for possible entry in interfaces of superclasses 231 // Check for possible entry in interfaces of superclasses
@@ -245,9 +238,9 @@ public class TranslationIndex {
245 238
246 // Doesn't exists in superclasses? check the child or return null 239 // Doesn't exists in superclasses? check the child or return null
247 if (!existInEntry) 240 if (!existInEntry)
248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 241 return !entryExists(originalEntry) ? null : originalEntry.getOwnerClassEntry();
249 242
250 return entry.getClassEntry(); 243 return entry.getOwnerClassEntry();
251 } 244 }
252 245
253 public ClassEntry resolveSuperclass(Entry entry) { 246 public ClassEntry resolveSuperclass(Entry entry) {
@@ -256,7 +249,7 @@ public class TranslationIndex {
256 249
257 while (!entryExists(entry)) { 250 while (!entryExists(entry)) {
258 // is there a parent class? 251 // is there a parent class?
259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 252 ClassEntry superclassEntry = getSuperclass(entry.getOwnerClassEntry());
260 if (superclassEntry == null) { 253 if (superclassEntry == null) {
261 // this is probably a method from a class in a library 254 // this is probably a method from a class in a library
262 // we can't trace the implementation up any higher unless we index the library 255 // we can't trace the implementation up any higher unless we index the library
@@ -264,23 +257,23 @@ public class TranslationIndex {
264 } 257 }
265 258
266 // move up to the parent class 259 // move up to the parent class
267 entry = entry.cloneToNewClass(superclassEntry); 260 entry = entry.updateOwnership(superclassEntry);
268 } 261 }
269 return entry.getClassEntry(); 262 return entry.getOwnerClassEntry();
270 } 263 }
271 264
272 public ClassEntry resolveInterface(Entry entry) { 265 public ClassEntry resolveInterface(Entry entry) {
273 // the interfaces for any class is a forest 266 // the interfaces for any class is a forest
274 // so let's look at all the trees 267 // so let's look at all the trees
275 268
276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 269 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getOwnerClassEntry())) {
277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 270 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 if (subInterface != null && !subInterface.isEmpty()) { 271 if (subInterface != null && !subInterface.isEmpty()) {
279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 272 ClassEntry result = resolveInterface(entry.updateOwnership(interfaceEntry));
280 if (result != null) 273 if (result != null)
281 return result; 274 return result;
282 } 275 }
283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 276 ClassEntry resolvedClassEntry = resolveSuperclass(entry.updateOwnership(interfaceEntry));
284 if (resolvedClassEntry != null) { 277 if (resolvedClassEntry != null) {
285 return resolvedClassEntry; 278 return resolvedClassEntry;
286 } 279 }