summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar Thog2017-03-08 08:17:04 +0100
committerGravatar Thog2017-03-08 08:17:04 +0100
commit6e464ea251cab63c776ece0b2a356f1498ffa294 (patch)
tree5ed30c03f5ac4cd2d6877874f5ede576049954f7 /src/main/java/cuchaz/enigma/analysis
parentDrop unix case style and implement hashCode when equals is overrided (diff)
downloadenigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.gz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.xz
enigma-fork-6e464ea251cab63c776ece0b2a356f1498ffa294.zip
Follow Fabric guidelines
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java59
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java131
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BridgeMarker.java35
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java95
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java105
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java207
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java232
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java107
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java194
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java1615
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java147
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java167
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java5
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java305
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java358
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java129
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java722
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java80
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java543
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java833
20 files changed, 3025 insertions, 3044 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index b8a7b2c..547d85e 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -8,40 +8,41 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import java.lang.reflect.Modifier; 12package cuchaz.enigma.analysis;
14 13
15import javassist.CtBehavior; 14import javassist.CtBehavior;
16import javassist.CtField; 15import javassist.CtField;
17 16
17import java.lang.reflect.Modifier;
18
18public enum Access { 19public enum Access {
19 20
20 PUBLIC, PROTECTED, PACKAGE, PRIVATE; 21 PUBLIC, PROTECTED, PACKAGE, PRIVATE;
21 22
22 public static Access get(CtBehavior behavior) { 23 public static Access get(CtBehavior behavior) {
23 return get(behavior.getModifiers()); 24 return get(behavior.getModifiers());
24 } 25 }
25 26
26 public static Access get(CtField field) { 27 public static Access get(CtField field) {
27 return get(field.getModifiers()); 28 return get(field.getModifiers());
28 } 29 }
29 30
30 public static Access get(int modifiers) { 31 public static Access get(int modifiers) {
31 boolean isPublic = Modifier.isPublic(modifiers); 32 boolean isPublic = Modifier.isPublic(modifiers);
32 boolean isProtected = Modifier.isProtected(modifiers); 33 boolean isProtected = Modifier.isProtected(modifiers);
33 boolean isPrivate = Modifier.isPrivate(modifiers); 34 boolean isPrivate = Modifier.isPrivate(modifiers);
34 35
35 if (isPublic && !isProtected && !isPrivate) { 36 if (isPublic && !isProtected && !isPrivate) {
36 return PUBLIC; 37 return PUBLIC;
37 } else if (!isPublic && isProtected && !isPrivate) { 38 } else if (!isPublic && isProtected && !isPrivate) {
38 return PROTECTED; 39 return PROTECTED;
39 } else if (!isPublic && !isProtected && isPrivate) { 40 } else if (!isPublic && !isProtected && isPrivate) {
40 return PRIVATE; 41 return PRIVATE;
41 } else if (!isPublic && !isProtected && !isPrivate) { 42 } else if (!isPublic && !isProtected && !isPrivate) {
42 return PACKAGE; 43 return PACKAGE;
43 } 44 }
44 // assume public by default 45 // assume public by default
45 return PUBLIC; 46 return PUBLIC;
46 } 47 }
47} 48}
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
index 52d5b31..6556b2c 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
@@ -20,85 +21,73 @@ import javax.swing.tree.TreeNode;
20import java.util.Set; 21import java.util.Set;
21 22
22public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode 23public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode
23 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> 24 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> {
24{
25 25
26 private Translator deobfuscatingTranslator; 26 private Translator deobfuscatingTranslator;
27 private BehaviorEntry entry; 27 private BehaviorEntry entry;
28 private EntryReference<BehaviorEntry, BehaviorEntry> reference; 28 private EntryReference<BehaviorEntry, BehaviorEntry> reference;
29 private Access access; 29 private Access access;
30 30
31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) 31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) {
32 { 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 33 this.entry = entry;
34 this.entry = entry; 34 this.reference = null;
35 this.reference = null; 35 }
36 }
37 36
38 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, 37 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator,
39 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) 38 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) {
40 { 39 this.deobfuscatingTranslator = deobfuscatingTranslator;
41 this.deobfuscatingTranslator = deobfuscatingTranslator; 40 this.entry = reference.entry;
42 this.entry = reference.entry; 41 this.reference = reference;
43 this.reference = reference; 42 this.access = access;
44 this.access = access; 43 }
45 }
46 44
47 @Override public BehaviorEntry getEntry() 45 @Override
48 { 46 public BehaviorEntry getEntry() {
49 return this.entry; 47 return this.entry;
50 } 48 }
51 49
52 @Override public EntryReference<BehaviorEntry, BehaviorEntry> getReference() 50 @Override
53 { 51 public EntryReference<BehaviorEntry, BehaviorEntry> getReference() {
54 return this.reference; 52 return this.reference;
55 } 53 }
56 54
57 @Override public String toString() 55 @Override
58 { 56 public String toString() {
59 if (this.reference != null) 57 if (this.reference != null) {
60 { 58 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context),
61 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), 59 this.access);
62 this.access); 60 }
63 } 61 return this.deobfuscatingTranslator.translateEntry(this.entry).toString();
64 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 62 }
65 }
66 63
67 public void load(JarIndex index, boolean recurse) 64 public void load(JarIndex index, boolean recurse) {
68 { 65 // get all the child nodes
69 // get all the child nodes 66 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) {
70 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) 67 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
71 { 68 }
72 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
73 }
74 69
75 if (recurse && this.children != null) 70 if (recurse && this.children != null) {
76 { 71 for (Object child : this.children) {
77 for (Object child : this.children) 72 if (child instanceof BehaviorReferenceTreeNode) {
78 { 73 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child;
79 if (child instanceof BehaviorReferenceTreeNode)
80 {
81 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child;
82 74
83 // don't recurse into ancestor 75 // don't recurse into ancestor
84 Set<Entry> ancestors = Sets.newHashSet(); 76 Set<Entry> ancestors = Sets.newHashSet();
85 TreeNode n = node; 77 TreeNode n = node;
86 while (n.getParent() != null) 78 while (n.getParent() != null) {
87 { 79 n = n.getParent();
88 n = n.getParent(); 80 if (n instanceof BehaviorReferenceTreeNode) {
89 if (n instanceof BehaviorReferenceTreeNode) 81 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry());
90 { 82 }
91 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); 83 }
92 } 84 if (ancestors.contains(node.getEntry())) {
93 } 85 continue;
94 if (ancestors.contains(node.getEntry())) 86 }
95 {
96 continue;
97 }
98 87
99 node.load(index, true); 88 node.load(index, true);
100 } 89 }
101 } 90 }
102 } 91 }
103 } 92 }
104} 93}
diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
index 0f4be7d..81e750c 100644
--- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
+++ b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import cuchaz.enigma.mapping.EntryFactory; 14import cuchaz.enigma.mapping.EntryFactory;
@@ -18,26 +19,26 @@ import javassist.bytecode.AccessFlag;
18 19
19public class BridgeMarker { 20public class BridgeMarker {
20 21
21 private JarIndex jarIndex; 22 private JarIndex jarIndex;
22 23
23 public BridgeMarker(JarIndex jarIndex) { 24 public BridgeMarker(JarIndex jarIndex) {
24 this.jarIndex = jarIndex; 25 this.jarIndex = jarIndex;
25 } 26 }
26 27
27 public void markBridges(CtClass c) { 28 public void markBridges(CtClass c) {
28 29
29 for (CtMethod method : c.getDeclaredMethods()) { 30 for (CtMethod method : c.getDeclaredMethods()) {
30 MethodEntry methodEntry = EntryFactory.getMethodEntry(method); 31 MethodEntry methodEntry = EntryFactory.getMethodEntry(method);
31 32
32 // is this a bridge method? 33 // is this a bridge method?
33 MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); 34 MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry);
34 if (bridgedMethodEntry != null) { 35 if (bridgedMethodEntry != null) {
35 36
36 // it's a bridge method! add the bridge flag 37 // it's a bridge method! add the bridge flag
37 int flags = method.getMethodInfo().getAccessFlags(); 38 int flags = method.getMethodInfo().getAccessFlags();
38 flags |= AccessFlag.BRIDGE; 39 flags |= AccessFlag.BRIDGE;
39 method.getMethodInfo().setAccessFlags(flags); 40 method.getMethodInfo().setAccessFlags(flags);
40 } 41 }
41 } 42 }
42 } 43 }
43} 44}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index 70ece24..f2fb2f8 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -8,69 +8,68 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private ClassEntry entry; 25 private ClassEntry entry;
27 26
28 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
29 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
30 this.entry = entry; 29 this.entry = entry;
31 } 30 }
32 31
33 public ClassEntry getClassEntry() { 32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
34 return this.entry; 33 // is this the node?
35 } 34 if (node.entry.equals(entry.getClassEntry())) {
35 return node;
36 }
36 37
37 public String getDeobfClassName() { 38 // recurse
38 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 39 for (int i = 0; i < node.getChildCount(); i++) {
39 } 40 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry);
41 if (foundNode != null) {
42 return foundNode;
43 }
44 }
45 return null;
46 }
40 47
41 @Override 48 public ClassEntry getClassEntry() {
42 public String toString() { 49 return this.entry;
43 String className = getDeobfClassName(); 50 }
44 if (className == null) {
45 className = this.entry.getClassName();
46 }
47 return className;
48 }
49 51
50 public void load(JarIndex index) { 52 public String getDeobfClassName() {
51 // get all method implementations 53 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
52 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); 54 }
53 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
54 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName)));
55 }
56 55
57 // add them to this node 56 @Override
58 nodes.forEach(this::add); 57 public String toString() {
59 } 58 String className = getDeobfClassName();
59 if (className == null) {
60 className = this.entry.getClassName();
61 }
62 return className;
63 }
60 64
61 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 65 public void load(JarIndex index) {
62 // is this the node? 66 // get all method implementations
63 if (node.entry.equals(entry.getClassEntry())) { 67 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList();
64 return node; 68 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
65 } 69 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName)));
70 }
66 71
67 // recurse 72 // add them to this node
68 for (int i = 0; i < node.getChildCount(); i++) { 73 nodes.forEach(this::add);
69 ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); 74 }
70 if (foundNode != null) {
71 return foundNode;
72 }
73 }
74 return null;
75 }
76} 75}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index 8a60fc7..24e7cb0 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -8,74 +8,73 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
21 17
18import javax.swing.tree.DefaultMutableTreeNode;
19import java.util.List;
20
22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
23 22
24 private Translator deobfuscatingTranslator; 23 private Translator deobfuscatingTranslator;
25 private String obfClassName; 24 private String obfClassName;
26 25
27 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.deobfuscatingTranslator = deobfuscatingTranslator;
29 this.obfClassName = obfClassName; 28 this.obfClassName = obfClassName;
30 } 29 }
31 30
32 public String getObfClassName() { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
33 return this.obfClassName; 32 // is this the node?
34 } 33 if (node.getObfClassName().equals(entry.getName())) {
34 return node;
35 }
35 36
36 public String getDeobfClassName() { 37 // recurse
37 return this.deobfuscatingTranslator.translateClass(this.obfClassName); 38 for (int i = 0; i < node.getChildCount(); i++) {
38 } 39 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry);
40 if (foundNode != null) {
41 return foundNode;
42 }
43 }
44 return null;
45 }
39 46
40 @Override 47 public String getObfClassName() {
41 public String toString() { 48 return this.obfClassName;
42 String deobfClassName = getDeobfClassName(); 49 }
43 if (deobfClassName != null) {
44 return deobfClassName;
45 }
46 return this.obfClassName;
47 }
48 50
49 public void load(TranslationIndex ancestries, boolean recurse) { 51 public String getDeobfClassName() {
50 // get all the child nodes 52 return this.deobfuscatingTranslator.translateClass(this.obfClassName);
51 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 53 }
52 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) {
53 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
54 }
55 54
56 // add them to this node 55 @Override
57 nodes.forEach(this::add); 56 public String toString() {
57 String deobfClassName = getDeobfClassName();
58 if (deobfClassName != null) {
59 return deobfClassName;
60 }
61 return this.obfClassName;
62 }
58 63
59 if (recurse) { 64 public void load(TranslationIndex ancestries, boolean recurse) {
60 for (ClassInheritanceTreeNode node : nodes) { 65 // get all the child nodes
61 node.load(ancestries, true); 66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
62 } 67 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) {
63 } 68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
64 } 69 }
65 70
66 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 71 // add them to this node
67 // is this the node? 72 nodes.forEach(this::add);
68 if (node.getObfClassName().equals(entry.getName())) {
69 return node;
70 }
71 73
72 // recurse 74 if (recurse) {
73 for (int i = 0; i < node.getChildCount(); i++) { 75 for (ClassInheritanceTreeNode node : nodes) {
74 ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); 76 node.load(ancestries, true);
75 if (foundNode != null) { 77 }
76 return foundNode; 78 }
77 } 79 }
78 }
79 return null;
80 }
81} 80}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index ad4baf8..3761fca 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -8,116 +8,117 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import java.util.Arrays; 12package cuchaz.enigma.analysis;
14import java.util.List;
15 13
16import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
17import cuchaz.enigma.mapping.ConstructorEntry; 15import cuchaz.enigma.mapping.ConstructorEntry;
18import cuchaz.enigma.mapping.Entry; 16import cuchaz.enigma.mapping.Entry;
19import cuchaz.enigma.utils.Utils; 17import cuchaz.enigma.utils.Utils;
20 18
19import java.util.Arrays;
20import java.util.List;
21
21public class EntryReference<E extends Entry, C extends Entry> { 22public class EntryReference<E extends Entry, C extends Entry> {
22 23
23 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); 24 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static");
24 public E entry; 25 public E entry;
25 public C context; 26 public C context;
26 27
27 private boolean sourceName; 28 private boolean sourceName;
28 29
29 public EntryReference(E entry, String sourceName) { 30 public EntryReference(E entry, String sourceName) {
30 this(entry, sourceName, null); 31 this(entry, sourceName, null);
31 } 32 }
32 33
33 public EntryReference(E entry, String sourceName, C context) { 34 public EntryReference(E entry, String sourceName, C context) {
34 if (entry == null) { 35 if (entry == null) {
35 throw new IllegalArgumentException("Entry cannot be null!"); 36 throw new IllegalArgumentException("Entry cannot be null!");
36 } 37 }
37 38
38 this.entry = entry; 39 this.entry = entry;
39 this.context = context; 40 this.context = context;
40 41
41 this.sourceName = sourceName != null && sourceName.length() > 0; 42 this.sourceName = sourceName != null && !sourceName.isEmpty();
42 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { 43 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) {
43 this.sourceName = false; 44 this.sourceName = false;
44 } 45 }
45 } 46 }
46 47
47 public EntryReference(E entry, C context, EntryReference<E, C> other) { 48 public EntryReference(E entry, C context, EntryReference<E, C> other) {
48 this.entry = entry; 49 this.entry = entry;
49 this.context = context; 50 this.context = context;
50 this.sourceName = other.sourceName; 51 this.sourceName = other.sourceName;
51 } 52 }
52 53
53 public ClassEntry getLocationClassEntry() { 54 public ClassEntry getLocationClassEntry() {
54 if (context != null) { 55 if (context != null) {
55 return context.getClassEntry(); 56 return context.getClassEntry();
56 } 57 }
57 return entry.getClassEntry(); 58 return entry.getClassEntry();
58 } 59 }
59 60
60 public boolean isNamed() { 61 public boolean isNamed() {
61 return this.sourceName; 62 return this.sourceName;
62 } 63 }
63 64
64 public Entry getNameableEntry() { 65 public Entry getNameableEntry() {
65 if (entry instanceof ConstructorEntry) { 66 if (entry instanceof ConstructorEntry) {
66 // renaming a constructor really means renaming the class 67 // renaming a constructor really means renaming the class
67 return entry.getClassEntry(); 68 return entry.getClassEntry();
68 } 69 }
69 return entry; 70 return entry;
70 } 71 }
71 72
72 public String getNamableName() { 73 public String getNamableName() {
73 if (getNameableEntry() instanceof ClassEntry) { 74 if (getNameableEntry() instanceof ClassEntry) {
74 ClassEntry classEntry = (ClassEntry) getNameableEntry(); 75 ClassEntry classEntry = (ClassEntry) getNameableEntry();
75 if (classEntry.isInnerClass()) { 76 if (classEntry.isInnerClass()) {
76 // make sure we only rename the inner class name 77 // make sure we only rename the inner class name
77 return classEntry.getInnermostClassName(); 78 return classEntry.getInnermostClassName();
78 } 79 }
79 } 80 }
80 81
81 return getNameableEntry().getName(); 82 return getNameableEntry().getName();
82 } 83 }
83 84
84 @Override 85 @Override
85 public int hashCode() { 86 public int hashCode() {
86 if (context != null) { 87 if (context != null) {
87 return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); 88 return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode());
88 } 89 }
89 return entry.hashCode(); 90 return entry.hashCode();
90 } 91 }
91 92
92 @Override 93 @Override
93 public boolean equals(Object other) { 94 public boolean equals(Object other) {
94 return other instanceof EntryReference && equals((EntryReference<?, ?>) other); 95 return other instanceof EntryReference && equals((EntryReference<?, ?>) other);
95 } 96 }
96 97
97 public boolean equals(EntryReference<?, ?> other) { 98 public boolean equals(EntryReference<?, ?> other) {
98 // check entry first 99 // check entry first
99 boolean isEntrySame = entry.equals(other.entry); 100 boolean isEntrySame = entry.equals(other.entry);
100 if (!isEntrySame) { 101 if (!isEntrySame) {
101 return false; 102 return false;
102 } 103 }
103 104
104 // check caller 105 // check caller
105 if (context == null && other.context == null) { 106 if (context == null && other.context == null) {
106 return true; 107 return true;
107 } else if (context != null && other.context != null) { 108 } else if (context != null && other.context != null) {
108 return context.equals(other.context); 109 return context.equals(other.context);
109 } 110 }
110 return false; 111 return false;
111 } 112 }
112 113
113 @Override 114 @Override
114 public String toString() { 115 public String toString() {
115 StringBuilder buf = new StringBuilder(); 116 StringBuilder buf = new StringBuilder();
116 buf.append(entry); 117 buf.append(entry);
117 if (context != null) { 118 if (context != null) {
118 buf.append(" called from "); 119 buf.append(" called from ");
119 buf.append(context); 120 buf.append(context);
120 } 121 }
121 return buf.toString(); 122 return buf.toString();
122 } 123 }
123} 124}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index 7233fcf..75806c3 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -8,140 +8,140 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
15import com.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.mapping.*;
16 18
17import java.util.AbstractMap; 19import java.util.AbstractMap;
18import java.util.List; 20import java.util.List;
19import java.util.Map; 21import java.util.Map;
20import java.util.Set; 22import java.util.Set;
21 23
22import cuchaz.enigma.mapping.*;
23
24public class EntryRenamer { 24public class EntryRenamer {
25 25
26 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) { 26 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) {
27 List<T> entries = Lists.newArrayList(); 27 List<T> entries = Lists.newArrayList();
28 for (T val : set) { 28 for (T val : set) {
29 entries.add(renameClassesInThing(renames, val)); 29 entries.add(renameClassesInThing(renames, val));
30 } 30 }
31 set.clear(); 31 set.clear();
32 set.addAll(entries); 32 set.addAll(entries);
33 } 33 }
34 34
35 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) { 35 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) {
36 // for each key/value pair... 36 // for each key/value pair...
37 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 37 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
38 for (Map.Entry<Key, Val> entry : map.entrySet()) { 38 for (Map.Entry<Key, Val> entry : map.entrySet()) {
39 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); 39 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
40 } 40 }
41 map.clear(); 41 map.clear();
42 for (Map.Entry<Key, Val> entry : entriesToAdd) { 42 for (Map.Entry<Key, Val> entry : entriesToAdd) {
43 map.put(entry.getKey(), entry.getValue()); 43 map.put(entry.getKey(), entry.getValue());
44 } 44 }
45 } 45 }
46 46
47 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) { 47 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) {
48 // for each key/value pair... 48 // for each key/value pair...
49 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 49 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
50 for (Map.Entry<Key, Val> entry : map.entries()) { 50 for (Map.Entry<Key, Val> entry : map.entries()) {
51 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); 51 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
52 } 52 }
53 map.clear(); 53 map.clear();
54 for (Map.Entry<Key, Val> entry : entriesToAdd) { 54 for (Map.Entry<Key, Val> entry : entriesToAdd) {
55 map.put(entry.getKey(), entry.getValue()); 55 map.put(entry.getKey(), entry.getValue());
56 } 56 }
57 } 57 }
58 58
59 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) { 59 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) {
60 // for each key/value pair... 60 // for each key/value pair...
61 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 61 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
62 for (Map.Entry<Key, Val> entry : map.entries()) { 62 for (Map.Entry<Key, Val> entry : map.entries()) {
63 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); 63 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
64 } 64 }
65 map.clear(); 65 map.clear();
66 for (Map.Entry<Key, Val> entry : entriesToAdd) { 66 for (Map.Entry<Key, Val> entry : entriesToAdd) {
67 map.put(entry.getKey(), entry.getValue()); 67 map.put(entry.getKey(), entry.getValue());
68 } 68 }
69 } 69 }
70 70
71 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) { 71 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) {
72 // for each key/value pair... 72 // for each key/value pair...
73 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); 73 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
74 for (Map.Entry<Key, Val> entry : map.entrySet()) { 74 for (Map.Entry<Key, Val> entry : map.entrySet()) {
75 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); 75 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
76 } 76 }
77 map.clear(); 77 map.clear();
78 for (Map.Entry<Key, Val> entry : entriesToAdd) { 78 for (Map.Entry<Key, Val> entry : entriesToAdd) {
79 map.put(entry.getKey(), entry.getValue()); 79 map.put(entry.getKey(), entry.getValue());
80 } 80 }
81 } 81 }
82 82
83 @SuppressWarnings("unchecked") 83 @SuppressWarnings("unchecked")
84 public static <T> T renameMethodsInThing(Map<MethodEntry,MethodEntry> renames, T thing) { 84 public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) {
85 if (thing instanceof MethodEntry) { 85 if (thing instanceof MethodEntry) {
86 MethodEntry methodEntry = (MethodEntry)thing; 86 MethodEntry methodEntry = (MethodEntry) thing;
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.getClassEntry(),
91 newMethodEntry.getName(), 91 newMethodEntry.getName(),
92 methodEntry.getSignature() 92 methodEntry.getSignature()
93 ); 93 );
94 } 94 }
95 return thing; 95 return thing;
96 } else if (thing instanceof ArgumentEntry) { 96 } else if (thing instanceof ArgumentEntry) {
97 ArgumentEntry argumentEntry = (ArgumentEntry)thing; 97 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
98 return (T)new ArgumentEntry( 98 return (T) new ArgumentEntry(
99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), 99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()),
100 argumentEntry.getIndex(), 100 argumentEntry.getIndex(),
101 argumentEntry.getName() 101 argumentEntry.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;
105 reference.entry = renameMethodsInThing(renames, reference.entry); 105 reference.entry = renameMethodsInThing(renames, reference.entry);
106 reference.context = renameMethodsInThing(renames, reference.context); 106 reference.context = renameMethodsInThing(renames, reference.context);
107 return thing; 107 return thing;
108 } 108 }
109 return thing; 109 return thing;
110 } 110 }
111 111
112 @SuppressWarnings("unchecked") 112 @SuppressWarnings("unchecked")
113 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { 113 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) {
114 if (thing instanceof String) { 114 if (thing instanceof String) {
115 String stringEntry = (String) thing; 115 String stringEntry = (String) thing;
116 if (renames.containsKey(stringEntry)) { 116 if (renames.containsKey(stringEntry)) {
117 return (T) renames.get(stringEntry); 117 return (T) renames.get(stringEntry);
118 } 118 }
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 FieldEntry) {
123 FieldEntry fieldEntry = (FieldEntry) thing; 123 FieldEntry fieldEntry = (FieldEntry) thing;
124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); 124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType()));
125 } else if (thing instanceof ConstructorEntry) { 125 } else if (thing instanceof ConstructorEntry) {
126 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 126 ConstructorEntry constructorEntry = (ConstructorEntry) thing;
127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); 127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature()));
128 } else if (thing instanceof MethodEntry) { 128 } else if (thing instanceof MethodEntry) {
129 MethodEntry methodEntry = (MethodEntry) thing; 129 MethodEntry methodEntry = (MethodEntry) thing;
130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); 130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature()));
131 } else if (thing instanceof ArgumentEntry) { 131 } else if (thing instanceof ArgumentEntry) {
132 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 132 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); 133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName());
134 } else if (thing instanceof EntryReference) { 134 } else if (thing instanceof EntryReference) {
135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
136 reference.entry = renameClassesInThing(renames, reference.entry); 136 reference.entry = renameClassesInThing(renames, reference.entry);
137 reference.context = renameClassesInThing(renames, reference.context); 137 reference.context = renameClassesInThing(renames, reference.context);
138 return thing; 138 return thing;
139 } else if (thing instanceof Signature) { 139 } else if (thing instanceof Signature) {
140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); 140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className));
141 } else if (thing instanceof Type) { 141 } else if (thing instanceof Type) {
142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); 142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className));
143 } 143 }
144 144
145 return thing; 145 return thing;
146 } 146 }
147} 147}
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 70cd059..34d2eff 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -8,72 +8,73 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12 11
13import javax.swing.tree.DefaultMutableTreeNode; 12package cuchaz.enigma.analysis;
14 13
15import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.BehaviorEntry;
16import cuchaz.enigma.mapping.FieldEntry; 15import cuchaz.enigma.mapping.FieldEntry;
17import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
18 17
18import javax.swing.tree.DefaultMutableTreeNode;
19
19public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 20public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> {
20 21
21 private Translator deobfuscatingTranslator; 22 private Translator deobfuscatingTranslator;
22 private FieldEntry entry; 23 private FieldEntry entry;
23 private EntryReference<FieldEntry, BehaviorEntry> reference; 24 private EntryReference<FieldEntry, BehaviorEntry> reference;
24 private Access access; 25 private Access access;
25 26
26 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 27 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
28 this.entry = entry; 29 this.entry = entry;
29 this.reference = null; 30 this.reference = null;
30 } 31 }
31 32
32 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { 33 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) {
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 34 this.deobfuscatingTranslator = deobfuscatingTranslator;
34 this.entry = reference.entry; 35 this.entry = reference.entry;
35 this.reference = reference; 36 this.reference = reference;
36 this.access = access; 37 this.access = access;
37 } 38 }
38 39
39 @Override 40 @Override
40 public FieldEntry getEntry() { 41 public FieldEntry getEntry() {
41 return this.entry; 42 return this.entry;
42 } 43 }
43 44
44 @Override 45 @Override
45 public EntryReference<FieldEntry, BehaviorEntry> getReference() { 46 public EntryReference<FieldEntry, BehaviorEntry> getReference() {
46 return this.reference; 47 return this.reference;
47 } 48 }
48 49
49 @Override 50 @Override
50 public String toString() { 51 public String toString() {
51 if (this.reference != null) { 52 if (this.reference != null) {
52 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); 53 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access);
53 } 54 }
54 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 55 return this.deobfuscatingTranslator.translateEntry(this.entry).toString();
55 } 56 }
56 57
57 public void load(JarIndex index, boolean recurse) { 58 public void load(JarIndex index, boolean recurse) {
58 // get all the child nodes 59 // get all the child nodes
59 if (this.reference == null) { 60 if (this.reference == null) {
60 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { 61 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) {
61 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 62 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
62 } 63 }
63 } else { 64 } else {
64 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { 65 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) {
65 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 66 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
66 } 67 }
67 } 68 }
68 69
69 if (recurse && children != null) { 70 if (recurse && children != null) {
70 for (Object node : children) { 71 for (Object node : children) {
71 if (node instanceof BehaviorReferenceTreeNode) { 72 if (node instanceof BehaviorReferenceTreeNode) {
72 ((BehaviorReferenceTreeNode) node).load(index, true); 73 ((BehaviorReferenceTreeNode) node).load(index, true);
73 } else if (node instanceof FieldReferenceTreeNode) { 74 } else if (node instanceof FieldReferenceTreeNode) {
74 ((FieldReferenceTreeNode) node).load(index, true); 75 ((FieldReferenceTreeNode) node).load(index, true);
75 } 76 }
76 } 77 }
77 } 78 }
78 } 79 }
79} 80}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
index 0d18105..87d3797 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
@@ -8,9 +8,17 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 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;
14 22
15import java.io.ByteArrayOutputStream; 23import java.io.ByteArrayOutputStream;
16import java.io.IOException; 24import java.io.IOException;
@@ -21,103 +29,95 @@ import java.util.List;
21import java.util.jar.JarEntry; 29import java.util.jar.JarEntry;
22import java.util.jar.JarFile; 30import java.util.jar.JarFile;
23 31
24import cuchaz.enigma.Constants;
25import cuchaz.enigma.mapping.ClassEntry;
26import javassist.ByteArrayClassPath;
27import javassist.ClassPool;
28import javassist.CtClass;
29import javassist.NotFoundException;
30import javassist.bytecode.Descriptor;
31
32public class JarClassIterator implements Iterator<CtClass> { 32public class JarClassIterator implements Iterator<CtClass> {
33 33
34 private JarFile jar; 34 private JarFile jar;
35 private Iterator<JarEntry> iter; 35 private Iterator<JarEntry> iter;
36 36
37 public JarClassIterator(JarFile jar) { 37 public JarClassIterator(JarFile jar) {
38 this.jar = jar; 38 this.jar = jar;
39 39
40 // get the jar entries that correspond to classes 40 // get the jar entries that correspond to classes
41 List<JarEntry> classEntries = Lists.newArrayList(); 41 List<JarEntry> classEntries = Lists.newArrayList();
42 Enumeration<JarEntry> entries = this.jar.entries(); 42 Enumeration<JarEntry> entries = this.jar.entries();
43 while (entries.hasMoreElements()) { 43 while (entries.hasMoreElements()) {
44 JarEntry entry = entries.nextElement(); 44 JarEntry entry = entries.nextElement();
45 45
46 // is this a class file? 46 // is this a class file?
47 if (entry.getName().endsWith(".class")) { 47 if (entry.getName().endsWith(".class")) {
48 classEntries.add(entry); 48 classEntries.add(entry);
49 } 49 }
50 } 50 }
51 this.iter = classEntries.iterator(); 51 this.iter = classEntries.iterator();
52 } 52 }
53 53
54 @Override 54 public static List<ClassEntry> getClassEntries(JarFile jar) {
55 public boolean hasNext() { 55 List<ClassEntry> classEntries = Lists.newArrayList();
56 return this.iter.hasNext(); 56 Enumeration<JarEntry> entries = jar.entries();
57 } 57 while (entries.hasMoreElements()) {
58 58 JarEntry entry = entries.nextElement();
59 @Override 59
60 public CtClass next() { 60 // is this a class file?
61 JarEntry entry = this.iter.next(); 61 if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
62 try { 62 classEntries.add(getClassEntry(entry));
63 return getClass(this.jar, entry); 63 }
64 } catch (IOException | NotFoundException ex) { 64 }
65 throw new Error("Unable to load class: " + entry.getName()); 65 return classEntries;
66 } 66 }
67 } 67
68 68 public static Iterable<CtClass> classes(final JarFile jar) {
69 @Override 69 return () -> new JarClassIterator(jar);
70 public void remove() { 70 }
71 throw new UnsupportedOperationException(); 71
72 } 72 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException {
73 73 // read the class into a buffer
74 public static List<ClassEntry> getClassEntries(JarFile jar) { 74 ByteArrayOutputStream bos = new ByteArrayOutputStream();
75 List<ClassEntry> classEntries = Lists.newArrayList(); 75 byte[] buf = new byte[Constants.KiB];
76 Enumeration<JarEntry> entries = jar.entries(); 76 int totalNumBytesRead = 0;
77 while (entries.hasMoreElements()) { 77 InputStream in = jar.getInputStream(entry);
78 JarEntry entry = entries.nextElement(); 78 while (in.available() > 0) {
79 79 int numBytesRead = in.read(buf);
80 // is this a class file? 80 if (numBytesRead < 0) {
81 if (!entry.isDirectory() && entry.getName().endsWith(".class")) { 81 break;
82 classEntries.add(getClassEntry(entry)); 82 }
83 } 83 bos.write(buf, 0, numBytesRead);
84 } 84
85 return classEntries; 85 // sanity checking
86 } 86 totalNumBytesRead += numBytesRead;
87 87 if (totalNumBytesRead > Constants.MiB) {
88 public static Iterable<CtClass> classes(final JarFile jar) { 88 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!");
89 return () -> new JarClassIterator(jar); 89 }
90 } 90 }
91 91
92 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { 92 // get a javassist handle for the class
93 // read the class into a buffer 93 String className = Descriptor.toJavaName(getClassEntry(entry).getName());
94 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 94 ClassPool classPool = new ClassPool();
95 byte[] buf = new byte[Constants.KiB]; 95 classPool.appendSystemPath();
96 int totalNumBytesRead = 0; 96 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray()));
97 InputStream in = jar.getInputStream(entry); 97 return classPool.get(className);
98 while (in.available() > 0) { 98 }
99 int numBytesRead = in.read(buf); 99
100 if (numBytesRead < 0) { 100 private static ClassEntry getClassEntry(JarEntry entry) {
101 break; 101 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length()));
102 } 102 }
103 bos.write(buf, 0, numBytesRead); 103
104 104 @Override
105 // sanity checking 105 public boolean hasNext() {
106 totalNumBytesRead += numBytesRead; 106 return this.iter.hasNext();
107 if (totalNumBytesRead > Constants.MiB) { 107 }
108 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); 108
109 } 109 @Override
110 } 110 public CtClass next() {
111 111 JarEntry entry = this.iter.next();
112 // get a javassist handle for the class 112 try {
113 String className = Descriptor.toJavaName(getClassEntry(entry).getName()); 113 return getClass(this.jar, entry);
114 ClassPool classPool = new ClassPool(); 114 } catch (IOException | NotFoundException ex) {
115 classPool.appendSystemPath(); 115 throw new Error("Unable to load class: " + entry.getName());
116 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); 116 }
117 return classPool.get(className); 117 }
118 } 118
119 119 @Override
120 private static ClassEntry getClassEntry(JarEntry entry) { 120 public void remove() {
121 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); 121 throw new UnsupportedOperationException();
122 } 122 }
123} 123}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index e8f74cc..ea87dda 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -8,825 +8,824 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.*; 14import com.google.common.collect.*;
14
15import java.lang.reflect.Modifier;
16import java.util.*;
17import java.util.jar.JarFile;
18
19import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.mapping.*;
20import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
21import javassist.*; 17import javassist.*;
22import javassist.bytecode.*; 18import javassist.bytecode.*;
23import javassist.expr.*; 19import javassist.expr.*;
24 20
21import java.lang.reflect.Modifier;
22import java.util.*;
23import java.util.jar.JarFile;
24
25public class JarIndex { 25public class JarIndex {
26 26
27 private Set<ClassEntry> obfClassEntries; 27 private Set<ClassEntry> obfClassEntries;
28 private TranslationIndex translationIndex; 28 private TranslationIndex translationIndex;
29 private Map<Entry, Access> access; 29 private Map<Entry, Access> access;
30 private Multimap<ClassEntry, FieldEntry> fields; 30 private Multimap<ClassEntry, FieldEntry> fields;
31 private Multimap<ClassEntry, BehaviorEntry> behaviors; 31 private Multimap<ClassEntry, BehaviorEntry> behaviors;
32 private Multimap<String, MethodEntry> methodImplementations; 32 private Multimap<String, MethodEntry> methodImplementations;
33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; 33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; 34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences;
35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
36 private Map<ClassEntry, ClassEntry> outerClassesByInner; 36 private Map<ClassEntry, ClassEntry> outerClassesByInner;
37 private Map<ClassEntry, BehaviorEntry> anonymousClasses; 37 private Map<ClassEntry, BehaviorEntry> anonymousClasses;
38 private Map<MethodEntry, MethodEntry> bridgedMethods; 38 private Map<MethodEntry, MethodEntry> bridgedMethods;
39 private Set<MethodEntry> syntheticMethods; 39 private Set<MethodEntry> syntheticMethods;
40 40
41 public JarIndex() { 41 public JarIndex() {
42 this.obfClassEntries = Sets.newHashSet(); 42 this.obfClassEntries = Sets.newHashSet();
43 this.translationIndex = new TranslationIndex(); 43 this.translationIndex = new TranslationIndex();
44 this.access = Maps.newHashMap(); 44 this.access = Maps.newHashMap();
45 this.fields = HashMultimap.create(); 45 this.fields = HashMultimap.create();
46 this.behaviors = HashMultimap.create(); 46 this.behaviors = HashMultimap.create();
47 this.methodImplementations = HashMultimap.create(); 47 this.methodImplementations = HashMultimap.create();
48 this.behaviorReferences = HashMultimap.create(); 48 this.behaviorReferences = HashMultimap.create();
49 this.fieldReferences = HashMultimap.create(); 49 this.fieldReferences = HashMultimap.create();
50 this.innerClassesByOuter = HashMultimap.create(); 50 this.innerClassesByOuter = HashMultimap.create();
51 this.outerClassesByInner = Maps.newHashMap(); 51 this.outerClassesByInner = Maps.newHashMap();
52 this.anonymousClasses = Maps.newHashMap(); 52 this.anonymousClasses = Maps.newHashMap();
53 this.bridgedMethods = Maps.newHashMap(); 53 this.bridgedMethods = Maps.newHashMap();
54 this.syntheticMethods = Sets.newHashSet(); 54 this.syntheticMethods = Sets.newHashSet();
55 } 55 }
56 56
57 public void indexJar(JarFile jar, boolean buildInnerClasses) { 57 public void indexJar(JarFile jar, boolean buildInnerClasses) {
58 58
59 // step 1: read the class names 59 // step 1: read the class names
60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); 60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar));
61 61
62 // step 2: index field/method/constructor access 62 // step 2: index field/method/constructor access
63 for (CtClass c : JarClassIterator.classes(jar)) { 63 for (CtClass c : JarClassIterator.classes(jar)) {
64 for (CtField field : c.getDeclaredFields()) { 64 for (CtField field : c.getDeclaredFields()) {
65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
66 this.access.put(fieldEntry, Access.get(field)); 66 this.access.put(fieldEntry, Access.get(field));
67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry); 67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry);
68 } 68 }
69 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 69 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
71 this.access.put(behaviorEntry, Access.get(behavior)); 71 this.access.put(behaviorEntry, Access.get(behavior));
72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); 72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry);
73 } 73 }
74 } 74 }
75 75
76 // step 3: index extends, implements, fields, and methods 76 // step 3: index extends, implements, fields, and methods
77 for (CtClass c : JarClassIterator.classes(jar)) { 77 for (CtClass c : JarClassIterator.classes(jar)) {
78 this.translationIndex.indexClass(c); 78 this.translationIndex.indexClass(c);
79 String className = Descriptor.toJvmName(c.getName()); 79 String className = Descriptor.toJvmName(c.getName());
80 for (String interfaceName : c.getClassFile().getInterfaces()) { 80 for (String interfaceName : c.getClassFile().getInterfaces()) {
81 className = Descriptor.toJvmName(className); 81 className = Descriptor.toJvmName(className);
82 interfaceName = Descriptor.toJvmName(interfaceName); 82 interfaceName = Descriptor.toJvmName(interfaceName);
83 if (className.equals(interfaceName)) { 83 if (className.equals(interfaceName)) {
84 throw new IllegalArgumentException("Class cannot be its own interface! " + className); 84 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
85 } 85 }
86 } 86 }
87 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 87 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
88 indexBehavior(behavior); 88 indexBehavior(behavior);
89 } 89 }
90 } 90 }
91 91
92 // step 4: index field, method, constructor references 92 // step 4: index field, method, constructor references
93 for (CtClass c : JarClassIterator.classes(jar)) { 93 for (CtClass c : JarClassIterator.classes(jar)) {
94 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 94 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
95 indexBehaviorReferences(behavior); 95 indexBehaviorReferences(behavior);
96 } 96 }
97 } 97 }
98 98
99 if (buildInnerClasses) { 99 if (buildInnerClasses) {
100 100
101 // step 5: index inner classes and anonymous classes 101 // step 5: index inner classes and anonymous classes
102 for (CtClass c : JarClassIterator.classes(jar)) { 102 for (CtClass c : JarClassIterator.classes(jar)) {
103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); 103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c);
104 ClassEntry outerClassEntry = findOuterClass(c); 104 ClassEntry outerClassEntry = findOuterClass(c);
105 if (outerClassEntry != null) { 105 if (outerClassEntry != null) {
106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); 106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry);
107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; 107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null;
108 assert (innerWasAdded); 108 assert (innerWasAdded);
109 109
110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); 110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry);
111 if (enclosingBehavior != null) { 111 if (enclosingBehavior != null) {
112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior); 112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior);
113 113
114 // DEBUG 114 // DEBUG
115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); 115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
116 }/* else { 116 }/* else {
117 // DEBUG 117 // DEBUG
118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); 118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
119 }*/ 119 }*/
120 } 120 }
121 } 121 }
122 122
123 // step 6: update other indices with inner class info 123 // step 6: update other indices with inner class info
124 Map<String, String> renames = Maps.newHashMap(); 124 Map<String, String> renames = Maps.newHashMap();
125 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { 125 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) {
126 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); 126 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName();
127 if (!innerClassEntry.getName().equals(newName)) { 127 if (!innerClassEntry.getName().equals(newName)) {
128 // DEBUG 128 // DEBUG
129 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); 129 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName);
130 renames.put(innerClassEntry.getName(), newName); 130 renames.put(innerClassEntry.getName(), newName);
131 } 131 }
132 } 132 }
133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
134 this.translationIndex.renameClasses(renames); 134 this.translationIndex.renameClasses(renames);
135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); 136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences);
137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
138 EntryRenamer.renameClassesInMap(renames, this.access); 138 EntryRenamer.renameClassesInMap(renames, this.access);
139 } 139 }
140 } 140 }
141 141
142 private void indexBehavior(CtBehavior behavior) { 142 private void indexBehavior(CtBehavior behavior) {
143 // get the behavior entry 143 // get the behavior entry
144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
145 if (behaviorEntry instanceof MethodEntry) { 145 if (behaviorEntry instanceof MethodEntry) {
146 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 146 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
147 147
148 // is synthetic 148 // is synthetic
149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { 149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) {
150 syntheticMethods.add(methodEntry); 150 syntheticMethods.add(methodEntry);
151 } 151 }
152 152
153 // index implementation 153 // index implementation
154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); 154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
155 155
156 // look for bridge and bridged methods 156 // look for bridge and bridged methods
157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); 157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior);
158 if (bridgedMethod != null) { 158 if (bridgedMethod != null) {
159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); 159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod));
160 } 160 }
161 } 161 }
162 // looks like we don't care about constructors here 162 // looks like we don't care about constructors here
163 } 163 }
164 164
165 private void indexBehaviorReferences(CtBehavior behavior) { 165 private void indexBehaviorReferences(CtBehavior behavior) {
166 // index method calls 166 // index method calls
167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
168 try { 168 try {
169 behavior.instrument(new ExprEditor() { 169 behavior.instrument(new ExprEditor() {
170 @Override 170 @Override
171 public void edit(MethodCall call) { 171 public void edit(MethodCall call) {
172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); 172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call);
173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); 173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry);
174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { 174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
175 calledMethodEntry = new MethodEntry( 175 calledMethodEntry = new MethodEntry(
176 resolvedClassEntry, 176 resolvedClassEntry,
177 calledMethodEntry.getName(), 177 calledMethodEntry.getName(),
178 calledMethodEntry.getSignature() 178 calledMethodEntry.getSignature()
179 ); 179 );
180 } 180 }
181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
182 calledMethodEntry, 182 calledMethodEntry,
183 call.getMethodName(), 183 call.getMethodName(),
184 behaviorEntry 184 behaviorEntry
185 ); 185 );
186 behaviorReferences.put(calledMethodEntry, reference); 186 behaviorReferences.put(calledMethodEntry, reference);
187 } 187 }
188 188
189 @Override 189 @Override
190 public void edit(FieldAccess call) { 190 public void edit(FieldAccess call) {
191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); 191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call);
192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); 192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry);
193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { 193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); 194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry);
195 } 195 }
196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>( 196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>(
197 calledFieldEntry, 197 calledFieldEntry,
198 call.getFieldName(), 198 call.getFieldName(),
199 behaviorEntry 199 behaviorEntry
200 ); 200 );
201 fieldReferences.put(calledFieldEntry, reference); 201 fieldReferences.put(calledFieldEntry, reference);
202 } 202 }
203 203
204 @Override 204 @Override
205 public void edit(ConstructorCall call) { 205 public void edit(ConstructorCall call) {
206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); 206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
208 calledConstructorEntry, 208 calledConstructorEntry,
209 call.getMethodName(), 209 call.getMethodName(),
210 behaviorEntry 210 behaviorEntry
211 ); 211 );
212 behaviorReferences.put(calledConstructorEntry, reference); 212 behaviorReferences.put(calledConstructorEntry, reference);
213 } 213 }
214 214
215 @Override 215 @Override
216 public void edit(NewExpr call) { 216 public void edit(NewExpr call) {
217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); 217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( 218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
219 calledConstructorEntry, 219 calledConstructorEntry,
220 call.getClassName(), 220 call.getClassName(),
221 behaviorEntry 221 behaviorEntry
222 ); 222 );
223 behaviorReferences.put(calledConstructorEntry, reference); 223 behaviorReferences.put(calledConstructorEntry, reference);
224 } 224 }
225 }); 225 });
226 } catch (CannotCompileException ex) { 226 } catch (CannotCompileException ex) {
227 throw new Error(ex); 227 throw new Error(ex);
228 } 228 }
229 } 229 }
230 230
231 private CtMethod getBridgedMethod(CtMethod method) { 231 private CtMethod getBridgedMethod(CtMethod method) {
232 232
233 // bridge methods just call another method, cast it to the return type, and return the result 233 // bridge methods just call another method, cast it to the return type, and return the result
234 // let's see if we can detect this scenario 234 // let's see if we can detect this scenario
235 235
236 // skip non-synthetic methods 236 // skip non-synthetic methods
237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { 237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
238 return null; 238 return null;
239 } 239 }
240 240
241 // get all the called methods 241 // get all the called methods
242 final List<MethodCall> methodCalls = Lists.newArrayList(); 242 final List<MethodCall> methodCalls = Lists.newArrayList();
243 try { 243 try {
244 method.instrument(new ExprEditor() { 244 method.instrument(new ExprEditor() {
245 @Override 245 @Override
246 public void edit(MethodCall call) { 246 public void edit(MethodCall call) {
247 methodCalls.add(call); 247 methodCalls.add(call);
248 } 248 }
249 }); 249 });
250 } catch (CannotCompileException ex) { 250 } catch (CannotCompileException ex) {
251 // this is stupid... we're not even compiling anything 251 // this is stupid... we're not even compiling anything
252 throw new Error(ex); 252 throw new Error(ex);
253 } 253 }
254 254
255 // is there just one? 255 // is there just one?
256 if (methodCalls.size() != 1) { 256 if (methodCalls.size() != 1) {
257 return null; 257 return null;
258 } 258 }
259 MethodCall call = methodCalls.get(0); 259 MethodCall call = methodCalls.get(0);
260 260
261 try { 261 try {
262 // we have a bridge method! 262 // we have a bridge method!
263 return call.getMethod(); 263 return call.getMethod();
264 } catch (NotFoundException ex) { 264 } catch (NotFoundException ex) {
265 // can't find the type? not a bridge method 265 // can't find the type? not a bridge method
266 return null; 266 return null;
267 } 267 }
268 } 268 }
269 269
270 private ClassEntry findOuterClass(CtClass c) { 270 private ClassEntry findOuterClass(CtClass c) {
271 271
272 ClassEntry classEntry = EntryFactory.getClassEntry(c); 272 ClassEntry classEntry = EntryFactory.getClassEntry(c);
273 273
274 // does this class already have an outer class? 274 // does this class already have an outer class?
275 if (classEntry.isInnerClass()) { 275 if (classEntry.isInnerClass()) {
276 return classEntry.getOuterClassEntry(); 276 return classEntry.getOuterClassEntry();
277 } 277 }
278 278
279 // inner classes: 279 // inner classes:
280 // have constructors that can (illegally) set synthetic fields 280 // have constructors that can (illegally) set synthetic fields
281 // the outer class is the only class that calls constructors 281 // the outer class is the only class that calls constructors
282 282
283 // use the synthetic fields to find the synthetic constructors 283 // use the synthetic fields to find the synthetic constructors
284 for (CtConstructor constructor : c.getDeclaredConstructors()) { 284 for (CtConstructor constructor : c.getDeclaredConstructors()) {
285 Set<String> syntheticFieldTypes = Sets.newHashSet(); 285 Set<String> syntheticFieldTypes = Sets.newHashSet();
286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { 286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
287 continue; 287 continue;
288 } 288 }
289 289
290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
291 291
292 // gather the classes from the illegally-set synthetic fields 292 // gather the classes from the illegally-set synthetic fields
293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); 293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
294 for (String type : syntheticFieldTypes) { 294 for (String type : syntheticFieldTypes) {
295 if (type.startsWith("L")) { 295 if (type.startsWith("L")) {
296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); 296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
297 if (isSaneOuterClass(outerClassEntry, classEntry)) { 297 if (isSaneOuterClass(outerClassEntry, classEntry)) {
298 illegallySetClasses.add(outerClassEntry); 298 illegallySetClasses.add(outerClassEntry);
299 } 299 }
300 } 300 }
301 } 301 }
302 302
303 // who calls this constructor? 303 // who calls this constructor?
304 Set<ClassEntry> callerClasses = Sets.newHashSet(); 304 Set<ClassEntry> callerClasses = Sets.newHashSet();
305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) { 305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
306 306
307 // make sure it's not a call to super 307 // make sure it's not a call to super
308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { 308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
309 309
310 // is the entry a superclass of the context? 310 // is the entry a superclass of the context?
311 ClassEntry calledClassEntry = reference.entry.getClassEntry(); 311 ClassEntry calledClassEntry = reference.entry.getClassEntry();
312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); 312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry());
313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { 313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
314 // it's a super call, skip 314 // it's a super call, skip
315 continue; 315 continue;
316 } 316 }
317 } 317 }
318 318
319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { 319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
320 callerClasses.add(reference.context.getClassEntry()); 320 callerClasses.add(reference.context.getClassEntry());
321 } 321 }
322 } 322 }
323 323
324 // do we have an answer yet? 324 // do we have an answer yet?
325 if (callerClasses.isEmpty()) { 325 if (callerClasses.isEmpty()) {
326 if (illegallySetClasses.size() == 1) { 326 if (illegallySetClasses.size() == 1) {
327 return illegallySetClasses.iterator().next(); 327 return illegallySetClasses.iterator().next();
328 } else { 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)); 329 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
330 } 330 }
331 } else { 331 } else {
332 if (callerClasses.size() == 1) { 332 if (callerClasses.size() == 1) {
333 return callerClasses.iterator().next(); 333 return callerClasses.iterator().next();
334 } else { 334 } else {
335 // multiple callers, do the illegally set classes narrow it down? 335 // multiple callers, do the illegally set classes narrow it down?
336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); 336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
337 intersection.retainAll(illegallySetClasses); 337 intersection.retainAll(illegallySetClasses);
338 if (intersection.size() == 1) { 338 if (intersection.size() == 1) {
339 return intersection.iterator().next(); 339 return intersection.iterator().next();
340 } else { 340 } else {
341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); 341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
342 } 342 }
343 } 343 }
344 } 344 }
345 } 345 }
346 346
347 return null; 347 return null;
348 } 348 }
349 349
350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { 350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) {
351 351
352 // clearly this would be silly 352 // clearly this would be silly
353 if (outerClassEntry.equals(innerClassEntry)) { 353 if (outerClassEntry.equals(innerClassEntry)) {
354 return false; 354 return false;
355 } 355 }
356 356
357 // is the outer class in the jar? 357 // is the outer class in the jar?
358 return this.obfClassEntries.contains(outerClassEntry); 358 return this.obfClassEntries.contains(outerClassEntry);
359 359
360 } 360 }
361 361
362 @SuppressWarnings("unchecked") 362 @SuppressWarnings("unchecked")
363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { 363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) {
364 364
365 // illegal constructors only set synthetic member fields, then call super() 365 // illegal constructors only set synthetic member fields, then call super()
366 String className = constructor.getDeclaringClass().getName(); 366 String className = constructor.getDeclaringClass().getName();
367 367
368 // collect all the field accesses, constructor calls, and method calls 368 // collect all the field accesses, constructor calls, and method calls
369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); 369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
370 final List<ConstructorCall> constructorCalls = Lists.newArrayList(); 370 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
371 try { 371 try {
372 constructor.instrument(new ExprEditor() { 372 constructor.instrument(new ExprEditor() {
373 @Override 373 @Override
374 public void edit(FieldAccess fieldAccess) { 374 public void edit(FieldAccess fieldAccess) {
375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { 375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
376 illegalFieldWrites.add(fieldAccess); 376 illegalFieldWrites.add(fieldAccess);
377 } 377 }
378 } 378 }
379 379
380 @Override 380 @Override
381 public void edit(ConstructorCall constructorCall) { 381 public void edit(ConstructorCall constructorCall) {
382 constructorCalls.add(constructorCall); 382 constructorCalls.add(constructorCall);
383 } 383 }
384 }); 384 });
385 } catch (CannotCompileException ex) { 385 } catch (CannotCompileException ex) {
386 // we're not compiling anything... this is stupid 386 // we're not compiling anything... this is stupid
387 throw new Error(ex); 387 throw new Error(ex);
388 } 388 }
389 389
390 // are there any illegal field writes? 390 // are there any illegal field writes?
391 if (illegalFieldWrites.isEmpty()) { 391 if (illegalFieldWrites.isEmpty()) {
392 return false; 392 return false;
393 } 393 }
394 394
395 // are all the writes to synthetic fields? 395 // are all the writes to synthetic fields?
396 for (FieldAccess fieldWrite : illegalFieldWrites) { 396 for (FieldAccess fieldWrite : illegalFieldWrites) {
397 397
398 // all illegal writes have to be to the local class 398 // all illegal writes have to be to the local class
399 if (!fieldWrite.getClassName().equals(className)) { 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())); 400 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
401 return false; 401 return false;
402 } 402 }
403 403
404 // find the field 404 // find the field
405 FieldInfo fieldInfo = null; 405 FieldInfo fieldInfo = null;
406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) { 406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) {
407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { 407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
408 fieldInfo = info; 408 fieldInfo = info;
409 break; 409 break;
410 } 410 }
411 } 411 }
412 if (fieldInfo == null) { 412 if (fieldInfo == null) {
413 // field is in a superclass or something, can't be a local synthetic member 413 // field is in a superclass or something, can't be a local synthetic member
414 return false; 414 return false;
415 } 415 }
416 416
417 // is this field synthetic? 417 // is this field synthetic?
418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
419 if (isSynthetic) { 419 if (isSynthetic) {
420 syntheticFieldTypes.add(fieldInfo.getDescriptor()); 420 syntheticFieldTypes.add(fieldInfo.getDescriptor());
421 } else { 421 } else {
422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); 422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
423 return false; 423 return false;
424 } 424 }
425 } 425 }
426 426
427 // we passed all the tests! 427 // we passed all the tests!
428 return true; 428 return true;
429 } 429 }
430 430
431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { 431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) {
432 432
433 // is this class already marked anonymous? 433 // is this class already marked anonymous?
434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
435 if (enclosingMethodAttribute != null) { 435 if (enclosingMethodAttribute != null) {
436 if (enclosingMethodAttribute.methodIndex() > 0) { 436 if (enclosingMethodAttribute.methodIndex() > 0) {
437 return EntryFactory.getBehaviorEntry( 437 return EntryFactory.getBehaviorEntry(
438 Descriptor.toJvmName(enclosingMethodAttribute.className()), 438 Descriptor.toJvmName(enclosingMethodAttribute.className()),
439 enclosingMethodAttribute.methodName(), 439 enclosingMethodAttribute.methodName(),
440 enclosingMethodAttribute.methodDescriptor() 440 enclosingMethodAttribute.methodDescriptor()
441 ); 441 );
442 } else { 442 } else {
443 // an attribute but no method? assume not anonymous 443 // an attribute but no method? assume not anonymous
444 return null; 444 return null;
445 } 445 }
446 } 446 }
447 447
448 // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous 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); 449 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
450 if (innerClassesAttribute != null) { 450 if (innerClassesAttribute != null) {
451 return null; 451 return null;
452 } 452 }
453 453
454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
455 455
456 // anonymous classes: 456 // anonymous classes:
457 // can't be abstract 457 // can't be abstract
458 // have only one constructor 458 // have only one constructor
459 // it's called exactly once by the outer class 459 // it's called exactly once by the outer class
460 // the type the instance is assigned to can't be this type 460 // the type the instance is assigned to can't be this type
461 461
462 // is abstract? 462 // is abstract?
463 if (Modifier.isAbstract(c.getModifiers())) { 463 if (Modifier.isAbstract(c.getModifiers())) {
464 return null; 464 return null;
465 } 465 }
466 466
467 // is there exactly one constructor? 467 // is there exactly one constructor?
468 if (c.getDeclaredConstructors().length != 1) { 468 if (c.getDeclaredConstructors().length != 1) {
469 return null; 469 return null;
470 } 470 }
471 CtConstructor constructor = c.getDeclaredConstructors()[0]; 471 CtConstructor constructor = c.getDeclaredConstructors()[0];
472 472
473 // is this constructor called exactly once? 473 // is this constructor called exactly once?
474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry); 475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
476 if (references.size() != 1) { 476 if (references.size() != 1) {
477 return null; 477 return null;
478 } 478 }
479 479
480 // does the caller use this type? 480 // does the caller use this type?
481 BehaviorEntry caller = references.iterator().next().context; 481 BehaviorEntry caller = references.iterator().next().context;
482 for (FieldEntry fieldEntry : getReferencedFields(caller)) { 482 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { 483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) {
484 // caller references this type, so it can't be anonymous 484 // caller references this type, so it can't be anonymous
485 return null; 485 return null;
486 } 486 }
487 } 487 }
488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { 488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { 489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) {
490 return null; 490 return null;
491 } 491 }
492 } 492 }
493 493
494 return caller; 494 return caller;
495 } 495 }
496 496
497 public Set<ClassEntry> getObfClassEntries() { 497 public Set<ClassEntry> getObfClassEntries() {
498 return this.obfClassEntries; 498 return this.obfClassEntries;
499 } 499 }
500 500
501 public Collection<FieldEntry> getObfFieldEntries() { 501 public Collection<FieldEntry> getObfFieldEntries() {
502 return this.fields.values(); 502 return this.fields.values();
503 } 503 }
504 504
505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { 505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) {
506 return this.fields.get(classEntry); 506 return this.fields.get(classEntry);
507 } 507 }
508 508
509 public Collection<BehaviorEntry> getObfBehaviorEntries() { 509 public Collection<BehaviorEntry> getObfBehaviorEntries() {
510 return this.behaviors.values(); 510 return this.behaviors.values();
511 } 511 }
512 512
513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { 513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) {
514 return this.behaviors.get(classEntry); 514 return this.behaviors.get(classEntry);
515 } 515 }
516 516
517 public TranslationIndex getTranslationIndex() { 517 public TranslationIndex getTranslationIndex() {
518 return this.translationIndex; 518 return this.translationIndex;
519 } 519 }
520 520
521 public Access getAccess(Entry entry) { 521 public Access getAccess(Entry entry) {
522 return this.access.get(entry); 522 return this.access.get(entry);
523 } 523 }
524 524
525 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { 525 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
526 526
527 // get the root node 527 // get the root node
528 List<String> ancestry = Lists.newArrayList(); 528 List<String> ancestry = Lists.newArrayList();
529 ancestry.add(obfClassEntry.getName()); 529 ancestry.add(obfClassEntry.getName());
530 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { 530 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) {
531 if (containsObfClass(classEntry)) { 531 if (containsObfClass(classEntry)) {
532 ancestry.add(classEntry.getName()); 532 ancestry.add(classEntry.getName());
533 } 533 }
534 } 534 }
535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
536 deobfuscatingTranslator, 536 deobfuscatingTranslator,
537 ancestry.get(ancestry.size() - 1) 537 ancestry.get(ancestry.size() - 1)
538 ); 538 );
539 539
540 // expand all children recursively 540 // expand all children recursively
541 rootNode.load(this.translationIndex, true); 541 rootNode.load(this.translationIndex, true);
542 542
543 return rootNode; 543 return rootNode;
544 } 544 }
545 545
546 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { 546 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
547 547
548 // is this even an interface? 548 // is this even an interface?
549 if (isInterface(obfClassEntry.getClassName())) { 549 if (isInterface(obfClassEntry.getClassName())) {
550 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); 550 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
551 node.load(this); 551 node.load(this);
552 return node; 552 return node;
553 } 553 }
554 return null; 554 return null;
555 } 555 }
556 556
557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
558 558
559 // travel to the ancestor implementation 559 // travel to the ancestor implementation
560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); 560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { 561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
562 MethodEntry ancestorMethodEntry = new MethodEntry( 562 MethodEntry ancestorMethodEntry = new MethodEntry(
563 new ClassEntry(ancestorClassEntry), 563 new ClassEntry(ancestorClassEntry),
564 obfMethodEntry.getName(), 564 obfMethodEntry.getName(),
565 obfMethodEntry.getSignature() 565 obfMethodEntry.getSignature()
566 ); 566 );
567 if (containsObfBehavior(ancestorMethodEntry)) { 567 if (containsObfBehavior(ancestorMethodEntry)) {
568 baseImplementationClassEntry = ancestorClassEntry; 568 baseImplementationClassEntry = ancestorClassEntry;
569 } 569 }
570 } 570 }
571 571
572 // make a root node at the base 572 // make a root node at the base
573 MethodEntry methodEntry = new MethodEntry( 573 MethodEntry methodEntry = new MethodEntry(
574 baseImplementationClassEntry, 574 baseImplementationClassEntry,
575 obfMethodEntry.getName(), 575 obfMethodEntry.getName(),
576 obfMethodEntry.getSignature() 576 obfMethodEntry.getSignature()
577 ); 577 );
578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
579 deobfuscatingTranslator, 579 deobfuscatingTranslator,
580 methodEntry, 580 methodEntry,
581 containsObfBehavior(methodEntry) 581 containsObfBehavior(methodEntry)
582 ); 582 );
583 583
584 // expand the full tree 584 // expand the full tree
585 rootNode.load(this, true); 585 rootNode.load(this, true);
586 586
587 return rootNode; 587 return rootNode;
588 } 588 }
589 589
590 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 590 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
591 591
592 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList(); 592 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList();
593 593
594 // is this method on an interface? 594 // is this method on an interface?
595 if (isInterface(obfMethodEntry.getClassName())) { 595 if (isInterface(obfMethodEntry.getClassName())) {
596 interfaceMethodEntries.add(obfMethodEntry); 596 interfaceMethodEntries.add(obfMethodEntry);
597 } else { 597 } else {
598 // get the interface class 598 // get the interface class
599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { 599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
600 600
601 // is this method defined in this interface? 601 // is this method defined in this interface?
602 MethodEntry methodInterface = new MethodEntry( 602 MethodEntry methodInterface = new MethodEntry(
603 interfaceEntry, 603 interfaceEntry,
604 obfMethodEntry.getName(), 604 obfMethodEntry.getName(),
605 obfMethodEntry.getSignature() 605 obfMethodEntry.getSignature()
606 ); 606 );
607 if (containsObfBehavior(methodInterface)) { 607 if (containsObfBehavior(methodInterface)) {
608 interfaceMethodEntries.add(methodInterface); 608 interfaceMethodEntries.add(methodInterface);
609 } 609 }
610 } 610 }
611 } 611 }
612 612
613 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 613 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
614 if (!interfaceMethodEntries.isEmpty()) { 614 if (!interfaceMethodEntries.isEmpty()) {
615 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { 615 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) {
616 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); 616 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
617 node.load(this); 617 node.load(this);
618 nodes.add(node); 618 nodes.add(node);
619 } 619 }
620 } 620 }
621 return nodes; 621 return nodes;
622 } 622 }
623 623
624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { 624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
625 Set<MethodEntry> methodEntries = Sets.newHashSet(); 625 Set<MethodEntry> methodEntries = Sets.newHashSet();
626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); 626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry));
627 return methodEntries; 627 return methodEntries;
628 } 628 }
629 629
630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
631 MethodEntry methodEntry = node.getMethodEntry(); 631 MethodEntry methodEntry = node.getMethodEntry();
632 632
633 if (containsObfBehavior(methodEntry)) { 633 if (containsObfBehavior(methodEntry)) {
634 // collect the entry 634 // collect the entry
635 methodEntries.add(methodEntry); 635 methodEntries.add(methodEntry);
636 } 636 }
637 637
638 // look at bridged methods! 638 // look at bridged methods!
639 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 639 MethodEntry bridgedEntry = getBridgedMethod(methodEntry);
640 while (bridgedEntry != null) { 640 while (bridgedEntry != null) {
641 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 641 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry));
642 bridgedEntry = getBridgedMethod(bridgedEntry); 642 bridgedEntry = getBridgedMethod(bridgedEntry);
643 } 643 }
644 644
645 // look at interface methods too 645 // look at interface methods too
646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { 646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) {
647 getRelatedMethodImplementations(methodEntries, implementationsNode); 647 getRelatedMethodImplementations(methodEntries, implementationsNode);
648 } 648 }
649 649
650 // recurse 650 // recurse
651 for (int i = 0; i < node.getChildCount(); i++) { 651 for (int i = 0; i < node.getChildCount(); i++) {
652 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); 652 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
653 } 653 }
654 } 654 }
655 655
656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
657 MethodEntry methodEntry = node.getMethodEntry(); 657 MethodEntry methodEntry = node.getMethodEntry();
658 if (containsObfBehavior(methodEntry)) { 658 if (containsObfBehavior(methodEntry)) {
659 // collect the entry 659 // collect the entry
660 methodEntries.add(methodEntry); 660 methodEntries.add(methodEntry);
661 } 661 }
662 662
663 // look at bridged methods! 663 // look at bridged methods!
664 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 664 MethodEntry bridgedEntry = getBridgedMethod(methodEntry);
665 while (bridgedEntry != null) { 665 while (bridgedEntry != null) {
666 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 666 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry));
667 bridgedEntry = getBridgedMethod(bridgedEntry); 667 bridgedEntry = getBridgedMethod(bridgedEntry);
668 } 668 }
669 669
670 // recurse 670 // recurse
671 for (int i = 0; i < node.getChildCount(); i++) { 671 for (int i = 0; i < node.getChildCount(); i++) {
672 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); 672 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
673 } 673 }
674 } 674 }
675 675
676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { 676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) {
677 return this.fieldReferences.get(fieldEntry); 677 return this.fieldReferences.get(fieldEntry);
678 } 678 }
679 679
680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { 680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) {
681 // linear search is fast enough for now 681 // linear search is fast enough for now
682 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 682 Set<FieldEntry> fieldEntries = Sets.newHashSet();
683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { 683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) {
684 if (reference.context == behaviorEntry) { 684 if (reference.context == behaviorEntry) {
685 fieldEntries.add(reference.entry); 685 fieldEntries.add(reference.entry);
686 } 686 }
687 } 687 }
688 return fieldEntries; 688 return fieldEntries;
689 } 689 }
690 690
691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { 691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) {
692 return this.behaviorReferences.get(behaviorEntry); 692 return this.behaviorReferences.get(behaviorEntry);
693 } 693 }
694 694
695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { 695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) {
696 // linear search is fast enough for now 696 // linear search is fast enough for now
697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); 697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { 698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) {
699 if (reference.context == behaviorEntry) { 699 if (reference.context == behaviorEntry) {
700 behaviorEntries.add(reference.entry); 700 behaviorEntries.add(reference.entry);
701 } 701 }
702 } 702 }
703 return behaviorEntries; 703 return behaviorEntries;
704 } 704 }
705 705
706 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { 706 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
707 return this.innerClassesByOuter.get(obfOuterClassEntry); 707 return this.innerClassesByOuter.get(obfOuterClassEntry);
708 } 708 }
709 709
710 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { 710 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) {
711 return this.outerClassesByInner.get(obfInnerClassEntry); 711 return this.outerClassesByInner.get(obfInnerClassEntry);
712 } 712 }
713 713
714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { 714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) {
715 return this.anonymousClasses.containsKey(obfInnerClassEntry); 715 return this.anonymousClasses.containsKey(obfInnerClassEntry);
716 } 716 }
717 717
718 public boolean isSyntheticMethod(MethodEntry methodEntry) { 718 public boolean isSyntheticMethod(MethodEntry methodEntry) {
719 return this.syntheticMethods.contains(methodEntry); 719 return this.syntheticMethods.contains(methodEntry);
720 } 720 }
721 721
722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { 722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) {
723 return this.anonymousClasses.get(obfInnerClassName); 723 return this.anonymousClasses.get(obfInnerClassName);
724 } 724 }
725 725
726 public Set<ClassEntry> getInterfaces(String className) { 726 public Set<ClassEntry> getInterfaces(String className) {
727 ClassEntry classEntry = new ClassEntry(className); 727 ClassEntry classEntry = new ClassEntry(className);
728 Set<ClassEntry> interfaces = new HashSet<>(); 728 Set<ClassEntry> interfaces = new HashSet<>();
729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); 729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry));
730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { 730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
731 interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); 731 interfaces.addAll(this.translationIndex.getInterfaces(ancestor));
732 } 732 }
733 return interfaces; 733 return interfaces;
734 } 734 }
735 735
736 public Set<String> getImplementingClasses(String targetInterfaceName) { 736 public Set<String> getImplementingClasses(String targetInterfaceName) {
737 737
738 // linear search is fast enough for now 738 // linear search is fast enough for now
739 Set<String> classNames = Sets.newHashSet(); 739 Set<String> classNames = Sets.newHashSet();
740 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) { 740 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) {
741 ClassEntry classEntry = entry.getKey(); 741 ClassEntry classEntry = entry.getKey();
742 ClassEntry interfaceEntry = entry.getValue(); 742 ClassEntry interfaceEntry = entry.getValue();
743 if (interfaceEntry.getName().equals(targetInterfaceName)) { 743 if (interfaceEntry.getName().equals(targetInterfaceName)) {
744 String className = classEntry.getClassName(); 744 String className = classEntry.getClassName();
745 classNames.add(className); 745 classNames.add(className);
746 if (isInterface(className)) { 746 if (isInterface(className)) {
747 classNames.addAll(getImplementingClasses(className)); 747 classNames.addAll(getImplementingClasses(className));
748 } 748 }
749 749
750 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); 750 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry);
751 } 751 }
752 } 752 }
753 return classNames; 753 return classNames;
754 } 754 }
755 755
756 public boolean isInterface(String className) { 756 public boolean isInterface(String className) {
757 return this.translationIndex.isInterface(new ClassEntry(className)); 757 return this.translationIndex.isInterface(new ClassEntry(className));
758 } 758 }
759 759
760 public boolean containsObfClass(ClassEntry obfClassEntry) { 760 public boolean containsObfClass(ClassEntry obfClassEntry) {
761 return this.obfClassEntries.contains(obfClassEntry); 761 return this.obfClassEntries.contains(obfClassEntry);
762 } 762 }
763 763
764 public boolean containsObfField(FieldEntry obfFieldEntry) { 764 public boolean containsObfField(FieldEntry obfFieldEntry) {
765 return this.access.containsKey(obfFieldEntry); 765 return this.access.containsKey(obfFieldEntry);
766 } 766 }
767 767
768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { 768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) {
769 return this.access.containsKey(obfBehaviorEntry); 769 return this.access.containsKey(obfBehaviorEntry);
770 } 770 }
771 771
772 public boolean containsEntryWithSameName(Entry entry) 772 public boolean containsEntryWithSameName(Entry entry) {
773 { 773 for (Entry target : this.access.keySet())
774 for (Entry target : this.access.keySet()) 774 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass()))
775 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) 775 return true;
776 return true; 776 return false;
777 return false; 777 }
778 } 778
779 779 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
780 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 780 // check the behavior
781 // check the behavior 781 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
782 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 782 return false;
783 return false; 783 }
784 } 784
785 785 // check the argument
786 // check the argument 786 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size();
787 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); 787
788 788 }
789 } 789
790 790 public boolean containsObfEntry(Entry obfEntry) {
791 public boolean containsObfEntry(Entry obfEntry) { 791 if (obfEntry instanceof ClassEntry) {
792 if (obfEntry instanceof ClassEntry) { 792 return containsObfClass((ClassEntry) obfEntry);
793 return containsObfClass((ClassEntry) obfEntry); 793 } else if (obfEntry instanceof FieldEntry) {
794 } else if (obfEntry instanceof FieldEntry) { 794 return containsObfField((FieldEntry) obfEntry);
795 return containsObfField((FieldEntry) obfEntry); 795 } else if (obfEntry instanceof BehaviorEntry) {
796 } else if (obfEntry instanceof BehaviorEntry) { 796 return containsObfBehavior((BehaviorEntry) obfEntry);
797 return containsObfBehavior((BehaviorEntry) obfEntry); 797 } else if (obfEntry instanceof ArgumentEntry) {
798 } else if (obfEntry instanceof ArgumentEntry) { 798 return containsObfArgument((ArgumentEntry) obfEntry);
799 return containsObfArgument((ArgumentEntry) obfEntry); 799 } else if (obfEntry instanceof LocalVariableEntry) {
800 } else if (obfEntry instanceof LocalVariableEntry) { 800 // TODO: Implement it
801 // TODO: Implement it 801 return false;
802 return false; 802 } else {
803 } else { 803 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
804 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 804 }
805 } 805 }
806 } 806
807 807 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) {
808 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { 808 return this.bridgedMethods.get(bridgeMethodEntry);
809 return this.bridgedMethods.get(bridgeMethodEntry); 809 }
810 } 810
811 811 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) {
812 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { 812
813 813 // build class chain in inner-to-outer order
814 // build class chain in inner-to-outer order 814 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry);
815 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); 815 ClassEntry checkClassEntry = obfClassEntry;
816 ClassEntry checkClassEntry = obfClassEntry; 816 while (true) {
817 while (true) { 817 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry);
818 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); 818 if (obfOuterClassEntry != null) {
819 if (obfOuterClassEntry != null) { 819 obfClassChain.add(obfOuterClassEntry);
820 obfClassChain.add(obfOuterClassEntry); 820 checkClassEntry = obfOuterClassEntry;
821 checkClassEntry = obfOuterClassEntry; 821 } else {
822 } else { 822 break;
823 break; 823 }
824 } 824 }
825 } 825
826 826 // switch to outer-to-inner order
827 // switch to outer-to-inner order 827 Collections.reverse(obfClassChain);
828 Collections.reverse(obfClassChain); 828
829 829 return obfClassChain;
830 return obfClassChain; 830 }
831 }
832} 831}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 9bd6219..bacb1aa 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -8,87 +8,86 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 22public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private MethodEntry entry; 25 private MethodEntry entry;
27 26
28 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 27 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) {
29 if (entry == null) { 28 if (entry == null) {
30 throw new IllegalArgumentException("Entry cannot be null!"); 29 throw new IllegalArgumentException("Entry cannot be null!");
31 } 30 }
32 31
33 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
34 this.entry = entry; 33 this.entry = entry;
35 } 34 }
36 35
37 public MethodEntry getMethodEntry() { 36 public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) {
38 return this.entry; 37 // is this the node?
39 } 38 if (node.getMethodEntry().equals(entry)) {
40 39 return node;
41 public String getDeobfClassName() { 40 }
42 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 41
43 } 42 // recurse
44 43 for (int i = 0; i < node.getChildCount(); i++) {
45 public String getDeobfMethodName() { 44 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry);
46 return this.deobfuscatingTranslator.translate(this.entry); 45 if (foundNode != null) {
47 } 46 return foundNode;
48 47 }
49 @Override 48 }
50 public String toString() { 49 return null;
51 String className = getDeobfClassName(); 50 }
52 if (className == null) { 51
53 className = this.entry.getClassName(); 52 public MethodEntry getMethodEntry() {
54 } 53 return this.entry;
55 54 }
56 String methodName = getDeobfMethodName(); 55
57 if (methodName == null) { 56 public String getDeobfClassName() {
58 methodName = this.entry.getName(); 57 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
59 } 58 }
60 return className + "." + methodName + "()"; 59
61 } 60 public String getDeobfMethodName() {
62 61 return this.deobfuscatingTranslator.translate(this.entry);
63 public void load(JarIndex index) { 62 }
64 63
65 // get all method implementations 64 @Override
66 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 65 public String toString() {
67 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 66 String className = getDeobfClassName();
68 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() 67 if (className == null) {
69 ); 68 className = this.entry.getClassName();
70 if (index.containsObfBehavior(methodEntry)) { 69 }
71 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 70
72 } 71 String methodName = getDeobfMethodName();
73 } 72 if (methodName == null) {
74 73 methodName = this.entry.getName();
75 // add them to this node 74 }
76 nodes.forEach(this::add); 75 return className + "." + methodName + "()";
77 } 76 }
78 77
79 public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { 78 public void load(JarIndex index) {
80 // is this the node? 79
81 if (node.getMethodEntry().equals(entry)) { 80 // get all method implementations
82 return node; 81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
83 } 82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
84 83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature()
85 // recurse 84 );
86 for (int i = 0; i < node.getChildCount(); i++) { 85 if (index.containsObfBehavior(methodEntry)) {
87 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); 86 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry));
88 if (foundNode != null) { 87 }
89 return foundNode; 88 }
90 } 89
91 } 90 // add them to this node
92 return null; 91 nodes.forEach(this::add);
93 } 92 }
94} 93}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index b65b8c1..4f84dd0 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -8,97 +8,96 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14
15import java.util.List;
16
17import javax.swing.tree.DefaultMutableTreeNode;
18
19import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.MethodEntry;
21import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
22 18
19import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List;
21
23public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 22public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
24 23
25 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
26 private MethodEntry entry; 25 private MethodEntry entry;
27 private boolean isImplemented; 26 private boolean isImplemented;
28 27
29 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { 28 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) {
30 this.deobfuscatingTranslator = deobfuscatingTranslator; 29 this.deobfuscatingTranslator = deobfuscatingTranslator;
31 this.entry = entry; 30 this.entry = entry;
32 this.isImplemented = isImplemented; 31 this.isImplemented = isImplemented;
33 } 32 }
34 33
35 public MethodEntry getMethodEntry() { 34 public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) {
36 return this.entry; 35 // is this the node?
37 } 36 if (node.getMethodEntry().equals(entry)) {
38 37 return node;
39 public String getDeobfClassName() { 38 }
40 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 39
41 } 40 // recurse
42 41 for (int i = 0; i < node.getChildCount(); i++) {
43 public String getDeobfMethodName() { 42 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry);
44 return this.deobfuscatingTranslator.translate(this.entry); 43 if (foundNode != null) {
45 } 44 return foundNode;
46 45 }
47 public boolean isImplemented() { 46 }
48 return this.isImplemented; 47 return null;
49 } 48 }
50 49
51 @Override 50 public MethodEntry getMethodEntry() {
52 public String toString() { 51 return this.entry;
53 String className = getDeobfClassName(); 52 }
54 if (className == null) { 53
55 className = this.entry.getClassName(); 54 public String getDeobfClassName() {
56 } 55 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName());
57 56 }
58 if (!this.isImplemented) { 57
59 return className; 58 public String getDeobfMethodName() {
60 } else { 59 return this.deobfuscatingTranslator.translate(this.entry);
61 String methodName = getDeobfMethodName(); 60 }
62 if (methodName == null) { 61
63 methodName = this.entry.getName(); 62 public boolean isImplemented() {
64 } 63 return this.isImplemented;
65 return className + "." + methodName + "()"; 64 }
66 } 65
67 } 66 @Override
68 67 public String toString() {
69 public void load(JarIndex index, boolean recurse) { 68 String className = getDeobfClassName();
70 // get all the child nodes 69 if (className == null) {
71 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 70 className = this.entry.getClassName();
72 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { 71 }
73 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() 72
74 ); 73 if (!this.isImplemented) {
75 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) 74 return className;
76 )); 75 } else {
77 } 76 String methodName = getDeobfMethodName();
78 77 if (methodName == null) {
79 // add them to this node 78 methodName = this.entry.getName();
80 nodes.forEach(this::add); 79 }
81 80 return className + "." + methodName + "()";
82 if (recurse) { 81 }
83 for (MethodInheritanceTreeNode node : nodes) { 82 }
84 node.load(index, true); 83
85 } 84 public void load(JarIndex index, boolean recurse) {
86 } 85 // get all the child nodes
87 } 86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
88 87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) {
89 public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { 88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature()
90 // is this the node? 89 );
91 if (node.getMethodEntry().equals(entry)) { 90 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry)
92 return node; 91 ));
93 } 92 }
94 93
95 // recurse 94 // add them to this node
96 for (int i = 0; i < node.getChildCount(); i++) { 95 nodes.forEach(this::add);
97 MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); 96
98 if (foundNode != null) { 97 if (recurse) {
99 return foundNode; 98 for (MethodInheritanceTreeNode node : nodes) {
100 } 99 node.load(index, true);
101 } 100 }
102 return null; 101 }
103 } 102 }
104} 103}
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index 9392346..0469363 100644
--- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -8,12 +8,13 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import cuchaz.enigma.mapping.Entry; 14import cuchaz.enigma.mapping.Entry;
14 15
15public interface ReferenceTreeNode<E extends Entry, C extends Entry> { 16public interface ReferenceTreeNode<E extends Entry, C extends Entry> {
16 E getEntry(); 17 E getEntry();
17 18
18 EntryReference<E, C> getReference(); 19 EntryReference<E, C> getReference();
19} 20}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 719930e..19250c8 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -8,174 +8,173 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
17
18import com.strobel.decompiler.languages.Region; 18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.Identifier; 20import com.strobel.decompiler.languages.java.ast.Identifier;
21import cuchaz.enigma.mapping.Entry;
21 22
22import java.util.Collection; 23import java.util.Collection;
23import java.util.List; 24import java.util.List;
24import java.util.Map; 25import java.util.Map;
25import java.util.TreeMap; 26import java.util.TreeMap;
26 27
27import cuchaz.enigma.mapping.Entry;
28
29public class SourceIndex { 28public class SourceIndex {
30 29
31 private String source; 30 private String source;
32 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; 31 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference;
33 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; 32 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens;
34 private Map<Entry, Token> declarationToToken; 33 private Map<Entry, Token> declarationToToken;
35 private List<Integer> lineOffsets; 34 private List<Integer> lineOffsets;
36 private boolean ignoreBadTokens; 35 private boolean ignoreBadTokens;
37 36
38 public SourceIndex(String source) { 37 public SourceIndex(String source) {
39 this(source, true); 38 this(source, true);
40 } 39 }
41 40
42 public SourceIndex(String source, boolean ignoreBadTokens) { 41 public SourceIndex(String source, boolean ignoreBadTokens) {
43 this.source = source; 42 this.source = source;
44 this.ignoreBadTokens = ignoreBadTokens; 43 this.ignoreBadTokens = ignoreBadTokens;
45 this.tokenToReference = Maps.newTreeMap(); 44 this.tokenToReference = Maps.newTreeMap();
46 this.referenceToTokens = HashMultimap.create(); 45 this.referenceToTokens = HashMultimap.create();
47 this.declarationToToken = Maps.newHashMap(); 46 this.declarationToToken = Maps.newHashMap();
48 this.lineOffsets = Lists.newArrayList(); 47 this.lineOffsets = Lists.newArrayList();
49 48
50 // count the lines 49 // count the lines
51 this.lineOffsets.add(0); 50 this.lineOffsets.add(0);
52 for (int i = 0; i < source.length(); i++) { 51 for (int i = 0; i < source.length(); i++) {
53 if (source.charAt(i) == '\n') { 52 if (source.charAt(i) == '\n') {
54 this.lineOffsets.add(i + 1); 53 this.lineOffsets.add(i + 1);
55 } 54 }
56 } 55 }
57 } 56 }
58 57
59 public String getSource() { 58 public String getSource() {
60 return this.source; 59 return this.source;
61 } 60 }
62 61
63 public Token getToken(AstNode node) { 62 public Token getToken(AstNode node) {
64 63
65 // get the text of the node 64 // get the text of the node
66 String name = ""; 65 String name = "";
67 if (node instanceof Identifier) { 66 if (node instanceof Identifier) {
68 name = ((Identifier) node).getName(); 67 name = ((Identifier) node).getName();
69 } 68 }
70 69
71 // get a token for this node's region 70 // get a token for this node's region
72 Region region = node.getRegion(); 71 Region region = node.getRegion();
73 if (region.getBeginLine() == 0 || region.getEndLine() == 0) { 72 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
74 // DEBUG 73 // DEBUG
75 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); 74 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
76 return null; 75 return null;
77 } 76 }
78 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); 77 Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source);
79 if (token.start == 0) { 78 if (token.start == 0) {
80 // DEBUG 79 // DEBUG
81 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); 80 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
82 return null; 81 return null;
83 } 82 }
84 83
85 // DEBUG 84 // DEBUG
86 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); 85 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
87 86
88 // if the token has a $ in it, something's wrong. Ignore this token 87 // if the token has a $ in it, something's wrong. Ignore this token
89 if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { 88 if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) {
90 // DEBUG 89 // DEBUG
91 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); 90 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
92 return null; 91 return null;
93 } 92 }
94 93
95 return token; 94 return token;
96 } 95 }
97 96
98 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { 97 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) {
99 Token token = getToken(node); 98 Token token = getToken(node);
100 if (token != null) { 99 if (token != null) {
101 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); 100 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
102 this.tokenToReference.put(token, deobfReference); 101 this.tokenToReference.put(token, deobfReference);
103 this.referenceToTokens.put(deobfReference, token); 102 this.referenceToTokens.put(deobfReference, token);
104 } 103 }
105 } 104 }
106 105
107 public void addDeclaration(AstNode node, Entry deobfEntry) { 106 public void addDeclaration(AstNode node, Entry deobfEntry) {
108 Token token = getToken(node); 107 Token token = getToken(node);
109 if (token != null) { 108 if (token != null) {
110 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); 109 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text);
111 this.tokenToReference.put(token, reference); 110 this.tokenToReference.put(token, reference);
112 this.referenceToTokens.put(reference, token); 111 this.referenceToTokens.put(reference, token);
113 this.declarationToToken.put(deobfEntry, token); 112 this.declarationToToken.put(deobfEntry, token);
114 } 113 }
115 } 114 }
116 115
117 public Token getReferenceToken(int pos) { 116 public Token getReferenceToken(int pos) {
118 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); 117 Token token = this.tokenToReference.floorKey(new Token(pos, pos, null));
119 if (token != null && token.contains(pos)) { 118 if (token != null && token.contains(pos)) {
120 return token; 119 return token;
121 } 120 }
122 return null; 121 return null;
123 } 122 }
124 123
125 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { 124 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) {
126 return this.referenceToTokens.get(deobfReference); 125 return this.referenceToTokens.get(deobfReference);
127 } 126 }
128 127
129 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 128 public EntryReference<Entry, Entry> getDeobfReference(Token token) {
130 if (token == null) { 129 if (token == null) {
131 return null; 130 return null;
132 } 131 }
133 return this.tokenToReference.get(token); 132 return this.tokenToReference.get(token);
134 } 133 }
135 134
136 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { 135 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) {
137 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); 136 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token);
138 this.tokenToReference.put(token, newDeobfReference); 137 this.tokenToReference.put(token, newDeobfReference);
139 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); 138 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference);
140 this.referenceToTokens.removeAll(oldDeobfReference); 139 this.referenceToTokens.removeAll(oldDeobfReference);
141 this.referenceToTokens.putAll(newDeobfReference, tokens); 140 this.referenceToTokens.putAll(newDeobfReference, tokens);
142 } 141 }
143 142
144 public Iterable<Token> referenceTokens() { 143 public Iterable<Token> referenceTokens() {
145 return this.tokenToReference.keySet(); 144 return this.tokenToReference.keySet();
146 } 145 }
147 146
148 public Iterable<Token> declarationTokens() { 147 public Iterable<Token> declarationTokens() {
149 return this.declarationToToken.values(); 148 return this.declarationToToken.values();
150 } 149 }
151 150
152 public Iterable<Entry> declarations() { 151 public Iterable<Entry> declarations() {
153 return this.declarationToToken.keySet(); 152 return this.declarationToToken.keySet();
154 } 153 }
155 154
156 public Token getDeclarationToken(Entry deobfEntry) { 155 public Token getDeclarationToken(Entry deobfEntry) {
157 return this.declarationToToken.get(deobfEntry); 156 return this.declarationToToken.get(deobfEntry);
158 } 157 }
159 158
160 public int getLineNumber(int pos) { 159 public int getLineNumber(int pos) {
161 // line number is 1-based 160 // line number is 1-based
162 int line = 0; 161 int line = 0;
163 for (Integer offset : this.lineOffsets) { 162 for (Integer offset : this.lineOffsets) {
164 if (offset > pos) { 163 if (offset > pos) {
165 break; 164 break;
166 } 165 }
167 line++; 166 line++;
168 } 167 }
169 return line; 168 return line;
170 } 169 }
171 170
172 public int getColumnNumber(int pos) { 171 public int getColumnNumber(int pos) {
173 // column number is 1-based 172 // column number is 1-based
174 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; 173 return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1;
175 } 174 }
176 175
177 private int toPos(int line, int col) { 176 private int toPos(int line, int col) {
178 // line and col are 1-based 177 // line and col are 1-based
179 return this.lineOffsets.get(line - 1) + col - 1; 178 return this.lineOffsets.get(line - 1) + col - 1;
180 } 179 }
181} 180}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
index bfd5a56..4febf25 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
@@ -26,186 +27,179 @@ import java.util.Map;
26 27
27public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 28public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
28 29
29 private BehaviorEntry behaviorEntry; 30 private BehaviorEntry behaviorEntry;
30 31
31 // TODO: Really fix Procyon index problem with inner classes 32 // TODO: Really fix Procyon index problem with inner classes
32 private int argumentPosition; 33 private int argumentPosition;
33 private int localsPosition; 34 private int localsPosition;
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 35 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 36 private Map<String, Entry> identifierEntryCache = new HashMap<>();
36 37
37 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { 38 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) {
38 this.behaviorEntry = behaviorEntry; 39 this.behaviorEntry = behaviorEntry;
39 this.argumentPosition = 0; 40 this.argumentPosition = 0;
40 this.localsPosition = 0; 41 this.localsPosition = 0;
41 } 42 }
42 43
43 @Override 44 @Override
44 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { 45 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
45 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 46 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
46 47
47 // get the behavior entry 48 // get the behavior entry
48 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 49 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
49 BehaviorEntry behaviorEntry = null; 50 BehaviorEntry behaviorEntry = null;
50 if (ref instanceof MethodReference) { 51 if (ref instanceof MethodReference) {
51 MethodReference methodRef = (MethodReference) ref; 52 MethodReference methodRef = (MethodReference) ref;
52 if (methodRef.isConstructor()) { 53 if (methodRef.isConstructor()) {
53 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 54 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
54 } else if (methodRef.isTypeInitializer()) { 55 } else if (methodRef.isTypeInitializer()) {
55 behaviorEntry = new ConstructorEntry(classEntry); 56 behaviorEntry = new ConstructorEntry(classEntry);
56 } else { 57 } else {
57 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); 58 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature()));
58 } 59 }
59 } 60 }
60 if (behaviorEntry != null) { 61 if (behaviorEntry != null) {
61 // get the node for the token 62 // get the node for the token
62 AstNode tokenNode = null; 63 AstNode tokenNode = null;
63 if (node.getTarget() instanceof MemberReferenceExpression) { 64 if (node.getTarget() instanceof MemberReferenceExpression) {
64 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); 65 tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken();
65 } else if (node.getTarget() instanceof SuperReferenceExpression) { 66 } else if (node.getTarget() instanceof SuperReferenceExpression) {
66 tokenNode = node.getTarget(); 67 tokenNode = node.getTarget();
67 } else if (node.getTarget() instanceof ThisReferenceExpression) { 68 } else if (node.getTarget() instanceof ThisReferenceExpression) {
68 tokenNode = node.getTarget(); 69 tokenNode = node.getTarget();
69 } 70 }
70 if (tokenNode != null) { 71 if (tokenNode != null) {
71 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); 72 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry);
72 } 73 }
73 } 74 }
74 75
75 // Check for identifier 76 // Check for identifier
76 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 77 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
77 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); 78 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
78 return recurse(node, index); 79 return recurse(node, index);
79 } 80 }
80 81
81 @Override 82 @Override
82 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 83 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
83 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 84 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
84 if (ref != null) { 85 if (ref != null) {
85 // make sure this is actually a field 86 // make sure this is actually a field
86 if (ref.getErasedSignature().indexOf('(') >= 0) { 87 if (ref.getErasedSignature().indexOf('(') >= 0) {
87 throw new Error("Expected a field here! got " + ref); 88 throw new Error("Expected a field here! got " + ref);
88 } 89 }
89 90
90 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 91 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
91 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 92 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature()));
92 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); 93 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry);
93 } 94 }
94 95
95 return recurse(node, index); 96 return recurse(node, index);
96 } 97 }
97 98
98 @Override 99 @Override
99 public Void visitSimpleType(SimpleType node, SourceIndex index) { 100 public Void visitSimpleType(SimpleType node, SourceIndex index) {
100 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 101 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
101 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 102 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
102 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 103 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
103 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); 104 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry);
104 } 105 }
105 106
106 return recurse(node, index); 107 return recurse(node, index);
107 } 108 }
108 109
109 @Override 110 @Override
110 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 111 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
111 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 112 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
112 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) 113 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) {
113 { 114 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()),
114 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), 115 argumentPosition++, node.getName());
115 argumentPosition++, node.getName()); 116 Identifier identifier = node.getNameToken();
116 Identifier identifier = node.getNameToken(); 117 // cache the argument entry and the identifier
117 // cache the argument entry and the identifier 118 identifierEntryCache.put(identifier.getName(), argumentEntry);
118 identifierEntryCache.put(identifier.getName(), argumentEntry); 119 index.addDeclaration(identifier, argumentEntry);
119 index.addDeclaration(identifier, argumentEntry); 120 }
120 } 121
121 122 return recurse(node, index);
122 return recurse(node, index); 123 }
123 } 124
124 125 @Override
125 @Override 126 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
126 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 127 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
127 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 128 if (ref != null) {
128 if (ref != null) { 129 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
129 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 130 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature()));
130 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 131 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry);
131 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); 132 } else
132 } 133 this.checkIdentifier(node, index);
133 else 134 return recurse(node, index);
134 this.checkIdentifier(node, index); 135 }
135 return recurse(node, index); 136
136 } 137 private void checkIdentifier(IdentifierExpression node, SourceIndex index) {
137 138 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
138 private void checkIdentifier(IdentifierExpression node, SourceIndex index) 139 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier()));
139 { 140 else
140 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! 141 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
141 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); 142 }
142 else 143
143 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! 144 private void addDeclarationToUnmatched(String key, SourceIndex index) {
144 } 145 Entry entry = identifierEntryCache.get(key);
145 146
146 private void addDeclarationToUnmatched(String key, SourceIndex index) 147 // This cannot happened in theory
147 { 148 if (entry == null)
148 Entry entry = identifierEntryCache.get(key); 149 return;
149 150 for (Identifier identifier : unmatchedIdentifier.get(key))
150 // This cannot happened in theory 151 index.addDeclaration(identifier, entry);
151 if (entry == null) 152 unmatchedIdentifier.removeAll(key);
152 return; 153 }
153 for (Identifier identifier : unmatchedIdentifier.get(key)) 154
154 index.addDeclaration(identifier, entry); 155 @Override
155 unmatchedIdentifier.removeAll(key); 156 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 } 157 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
157 158 if (ref != null) {
158 @Override 159 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
159 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 160 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
160 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 161 if (node.getType() instanceof SimpleType) {
161 if (ref != null) { 162 SimpleType simpleTypeNode = (SimpleType) node.getType();
162 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 163 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry);
163 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 164 }
164 if (node.getType() instanceof SimpleType) { 165 }
165 SimpleType simpleTypeNode = (SimpleType) node.getType(); 166
166 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); 167 return recurse(node, index);
167 } 168 }
168 } 169
169 170 @Override
170 return recurse(node, index); 171 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
171 } 172 if (node.getVariableType() instanceof SimpleType) {
172 173 SimpleType type = (SimpleType) node.getVariableType();
173 @Override 174 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
174 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 175 Identifier identifier = node.getVariableNameToken();
175 if (node.getVariableType() instanceof SimpleType) 176 String signature = Descriptor.of(typeReference.getErasedDescription());
176 { 177 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature));
177 SimpleType type = (SimpleType) node.getVariableType(); 178 identifierEntryCache.put(identifier.getName(), localVariableEntry);
178 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); 179 addDeclarationToUnmatched(identifier.getName(), index);
179 Identifier identifier = node.getVariableNameToken(); 180 index.addDeclaration(identifier, localVariableEntry);
180 String signature = Descriptor.of(typeReference.getErasedDescription()); 181 }
181 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); 182 return recurse(node, index);
182 identifierEntryCache.put(identifier.getName(), localVariableEntry); 183 }
183 addDeclarationToUnmatched(identifier.getName(), index); 184
184 index.addDeclaration(identifier, localVariableEntry); 185 @Override
185 } 186 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
186 return recurse(node, index); 187 AstNodeCollection<VariableInitializer> variables = node.getVariables();
187 } 188
188 189 // Single assignation
189 @Override 190 if (variables.size() == 1) {
190 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { 191 VariableInitializer initializer = variables.firstOrNullObject();
191 AstNodeCollection<VariableInitializer> variables = node.getVariables(); 192 if (initializer != null && node.getType() instanceof SimpleType) {
192 193 SimpleType type = (SimpleType) node.getType();
193 // Single assignation 194 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
194 if (variables.size() == 1) 195 String signature = Descriptor.of(typeReference.getErasedDescription());
195 { 196 Identifier identifier = initializer.getNameToken();
196 VariableInitializer initializer = variables.firstOrNullObject(); 197 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature));
197 if (initializer != null && node.getType() instanceof SimpleType) 198 identifierEntryCache.put(identifier.getName(), localVariableEntry);
198 { 199 addDeclarationToUnmatched(identifier.getName(), index);
199 SimpleType type = (SimpleType) node.getType(); 200 index.addDeclaration(identifier, localVariableEntry);
200 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); 201 }
201 String signature = Descriptor.of(typeReference.getErasedDescription()); 202 }
202 Identifier identifier = initializer.getNameToken(); 203 return recurse(node, index);
203 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); 204 }
204 identifierEntryCache.put(identifier.getName(), localVariableEntry);
205 addDeclarationToUnmatched(identifier.getName(), index);
206 index.addDeclaration(identifier, localVariableEntry);
207 }
208 }
209 return recurse(node, index);
210 }
211} 205}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index 2a21222..1148216 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.assembler.metadata.FieldDefinition; 14import com.strobel.assembler.metadata.FieldDefinition;
@@ -20,79 +21,79 @@ import cuchaz.enigma.mapping.*;
20 21
21public class SourceIndexClassVisitor extends SourceIndexVisitor { 22public class SourceIndexClassVisitor extends SourceIndexVisitor {
22 23
23 private ClassEntry classEntry; 24 private ClassEntry classEntry;
24 25
25 public SourceIndexClassVisitor(ClassEntry classEntry) { 26 public SourceIndexClassVisitor(ClassEntry classEntry) {
26 this.classEntry = classEntry; 27 this.classEntry = classEntry;
27 } 28 }
28 29
29 @Override 30 @Override
30 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 31 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
31 // is this this class, or a subtype? 32 // is this this class, or a subtype?
32 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 33 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
33 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 34 ClassEntry classEntry = new ClassEntry(def.getInternalName());
34 if (!classEntry.equals(this.classEntry)) { 35 if (!classEntry.equals(this.classEntry)) {
35 // it's a sub-type, recurse 36 // it's a sub-type, recurse
36 index.addDeclaration(node.getNameToken(), classEntry); 37 index.addDeclaration(node.getNameToken(), classEntry);
37 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 38 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
38 } 39 }
39 40
40 return recurse(node, index); 41 return recurse(node, index);
41 } 42 }
42 43
43 @Override 44 @Override
44 public Void visitSimpleType(SimpleType node, SourceIndex index) { 45 public Void visitSimpleType(SimpleType node, SourceIndex index) {
45 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 46 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
46 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 47 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
47 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 48 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
48 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); 49 index.addReference(node.getIdentifierToken(), classEntry, this.classEntry);
49 } 50 }
50 51
51 return recurse(node, index); 52 return recurse(node, index);
52 } 53 }
53 54
54 @Override 55 @Override
55 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 56 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
56 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 57 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
57 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); 58 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def);
58 AstNode tokenNode = node.getNameToken(); 59 AstNode tokenNode = node.getNameToken();
59 if (behaviorEntry instanceof ConstructorEntry) { 60 if (behaviorEntry instanceof ConstructorEntry) {
60 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; 61 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry;
61 if (constructorEntry.isStatic()) { 62 if (constructorEntry.isStatic()) {
62 // for static initializers, check elsewhere for the token node 63 // for static initializers, check elsewhere for the token node
63 tokenNode = node.getModifiers().firstOrNullObject(); 64 tokenNode = node.getModifiers().firstOrNullObject();
64 } 65 }
65 } 66 }
66 index.addDeclaration(tokenNode, behaviorEntry); 67 index.addDeclaration(tokenNode, behaviorEntry);
67 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); 68 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index);
68 } 69 }
69 70
70 @Override 71 @Override
71 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 72 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
72 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 73 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
73 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); 74 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def);
74 index.addDeclaration(node.getNameToken(), constructorEntry); 75 index.addDeclaration(node.getNameToken(), constructorEntry);
75 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); 76 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index);
76 } 77 }
77 78
78 @Override 79 @Override
79 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 80 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
80 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 81 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
81 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 82 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def);
82 assert (node.getVariables().size() == 1); 83 assert (node.getVariables().size() == 1);
83 VariableInitializer variable = node.getVariables().firstOrNullObject(); 84 VariableInitializer variable = node.getVariables().firstOrNullObject();
84 index.addDeclaration(variable.getNameToken(), fieldEntry); 85 index.addDeclaration(variable.getNameToken(), fieldEntry);
85 86
86 return recurse(node, index); 87 return recurse(node, index);
87 } 88 }
88 89
89 @Override 90 @Override
90 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 91 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
91 // treat enum declarations as field declarations 92 // treat enum declarations as field declarations
92 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 93 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
93 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 94 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def);
94 index.addDeclaration(node.getNameToken(), fieldEntry); 95 index.addDeclaration(node.getNameToken(), fieldEntry);
95 96
96 return recurse(node, index); 97 return recurse(node, index);
97 } 98 }
98} 99}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index 40381f4..a94a55b 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -8,374 +8,374 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.assembler.metadata.TypeDefinition; 14import com.strobel.assembler.metadata.TypeDefinition;
14import com.strobel.decompiler.languages.java.ast.*; 15import com.strobel.decompiler.languages.java.ast.*;
15import com.strobel.decompiler.patterns.Pattern; 16import com.strobel.decompiler.patterns.Pattern;
16
17import cuchaz.enigma.mapping.ClassEntry; 17import cuchaz.enigma.mapping.ClassEntry;
18 18
19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
20 20
21 @Override 21 @Override
22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
23 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 23 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
24 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 24 ClassEntry classEntry = new ClassEntry(def.getInternalName());
25 index.addDeclaration(node.getNameToken(), classEntry); 25 index.addDeclaration(node.getNameToken(), classEntry);
26 26
27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
28 } 28 }
29 29
30 protected Void recurse(AstNode node, SourceIndex index) { 30 protected Void recurse(AstNode node, SourceIndex index) {
31 for (final AstNode child : node.getChildren()) { 31 for (final AstNode child : node.getChildren()) {
32 child.acceptVisitor(this, index); 32 child.acceptVisitor(this, index);
33 } 33 }
34 return null; 34 return null;
35 } 35 }
36 36
37 @Override 37 @Override
38 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 38 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
39 return recurse(node, index); 39 return recurse(node, index);
40 } 40 }
41 41
42 @Override 42 @Override
43 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 43 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
44 return recurse(node, index); 44 return recurse(node, index);
45 } 45 }
46 46
47 @Override 47 @Override
48 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 48 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
49 return recurse(node, index); 49 return recurse(node, index);
50 } 50 }
51 51
52 @Override 52 @Override
53 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 53 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
54 return recurse(node, index); 54 return recurse(node, index);
55 } 55 }
56 56
57 @Override 57 @Override
58 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 58 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
59 return recurse(node, index); 59 return recurse(node, index);
60 } 60 }
61 61
62 @Override 62 @Override
63 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { 63 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
64 return recurse(node, index); 64 return recurse(node, index);
65 } 65 }
66 66
67 @Override 67 @Override
68 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 68 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
69 return recurse(node, index); 69 return recurse(node, index);
70 } 70 }
71 71
72 @Override 72 @Override
73 public Void visitSimpleType(SimpleType node, SourceIndex index) { 73 public Void visitSimpleType(SimpleType node, SourceIndex index) {
74 return recurse(node, index); 74 return recurse(node, index);
75 } 75 }
76 76
77 @Override 77 @Override
78 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 78 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
79 return recurse(node, index); 79 return recurse(node, index);
80 } 80 }
81 81
82 @Override 82 @Override
83 public Void visitComment(Comment node, SourceIndex index) { 83 public Void visitComment(Comment node, SourceIndex index) {
84 return recurse(node, index); 84 return recurse(node, index);
85 } 85 }
86 86
87 @Override 87 @Override
88 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { 88 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) {
89 return recurse(node, index); 89 return recurse(node, index);
90 } 90 }
91 91
92 @Override 92 @Override
93 public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { 93 public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) {
94 return recurse(node, index); 94 return recurse(node, index);
95 } 95 }
96 96
97 @Override 97 @Override
98 public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { 98 public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) {
99 return recurse(node, index); 99 return recurse(node, index);
100 } 100 }
101 101
102 @Override 102 @Override
103 public Void visitIdentifier(Identifier node, SourceIndex index) { 103 public Void visitIdentifier(Identifier node, SourceIndex index) {
104 return recurse(node, index); 104 return recurse(node, index);
105 } 105 }
106 106
107 @Override 107 @Override
108 public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { 108 public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) {
109 return recurse(node, index); 109 return recurse(node, index);
110 } 110 }
111 111
112 @Override 112 @Override
113 public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { 113 public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) {
114 return recurse(node, index); 114 return recurse(node, index);
115 } 115 }
116 116
117 @Override 117 @Override
118 public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { 118 public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) {
119 return recurse(node, index); 119 return recurse(node, index);
120 } 120 }
121 121
122 @Override 122 @Override
123 public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { 123 public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) {
124 return recurse(node, index); 124 return recurse(node, index);
125 } 125 }
126 126
127 @Override 127 @Override
128 public Void visitBlockStatement(BlockStatement node, SourceIndex index) { 128 public Void visitBlockStatement(BlockStatement node, SourceIndex index) {
129 return recurse(node, index); 129 return recurse(node, index);
130 } 130 }
131 131
132 @Override 132 @Override
133 public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { 133 public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) {
134 return recurse(node, index); 134 return recurse(node, index);
135 } 135 }
136 136
137 @Override 137 @Override
138 public Void visitBreakStatement(BreakStatement node, SourceIndex index) { 138 public Void visitBreakStatement(BreakStatement node, SourceIndex index) {
139 return recurse(node, index); 139 return recurse(node, index);
140 } 140 }
141 141
142 @Override 142 @Override
143 public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { 143 public Void visitContinueStatement(ContinueStatement node, SourceIndex index) {
144 return recurse(node, index); 144 return recurse(node, index);
145 } 145 }
146 146
147 @Override 147 @Override
148 public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { 148 public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) {
149 return recurse(node, index); 149 return recurse(node, index);
150 } 150 }
151 151
152 @Override 152 @Override
153 public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { 153 public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) {
154 return recurse(node, index); 154 return recurse(node, index);
155 } 155 }
156 156
157 @Override 157 @Override
158 public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { 158 public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) {
159 return recurse(node, index); 159 return recurse(node, index);
160 } 160 }
161 161
162 @Override 162 @Override
163 public Void visitLabelStatement(LabelStatement node, SourceIndex index) { 163 public Void visitLabelStatement(LabelStatement node, SourceIndex index) {
164 return recurse(node, index); 164 return recurse(node, index);
165 } 165 }
166 166
167 @Override 167 @Override
168 public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { 168 public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) {
169 return recurse(node, index); 169 return recurse(node, index);
170 } 170 }
171 171
172 @Override 172 @Override
173 public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { 173 public Void visitReturnStatement(ReturnStatement node, SourceIndex index) {
174 return recurse(node, index); 174 return recurse(node, index);
175 } 175 }
176 176
177 @Override 177 @Override
178 public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { 178 public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) {
179 return recurse(node, index); 179 return recurse(node, index);
180 } 180 }
181 181
182 @Override 182 @Override
183 public Void visitSwitchSection(SwitchSection node, SourceIndex index) { 183 public Void visitSwitchSection(SwitchSection node, SourceIndex index) {
184 return recurse(node, index); 184 return recurse(node, index);
185 } 185 }
186 186
187 @Override 187 @Override
188 public Void visitCaseLabel(CaseLabel node, SourceIndex index) { 188 public Void visitCaseLabel(CaseLabel node, SourceIndex index) {
189 return recurse(node, index); 189 return recurse(node, index);
190 } 190 }
191 191
192 @Override 192 @Override
193 public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { 193 public Void visitThrowStatement(ThrowStatement node, SourceIndex index) {
194 return recurse(node, index); 194 return recurse(node, index);
195 } 195 }
196 196
197 @Override 197 @Override
198 public Void visitCatchClause(CatchClause node, SourceIndex index) { 198 public Void visitCatchClause(CatchClause node, SourceIndex index) {
199 return recurse(node, index); 199 return recurse(node, index);
200 } 200 }
201 201
202 @Override 202 @Override
203 public Void visitAnnotation(Annotation node, SourceIndex index) { 203 public Void visitAnnotation(Annotation node, SourceIndex index) {
204 return recurse(node, index); 204 return recurse(node, index);
205 } 205 }
206 206
207 @Override 207 @Override
208 public Void visitNewLine(NewLineNode node, SourceIndex index) { 208 public Void visitNewLine(NewLineNode node, SourceIndex index) {
209 return recurse(node, index); 209 return recurse(node, index);
210 } 210 }
211 211
212 @Override 212 @Override
213 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { 213 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
214 return recurse(node, index); 214 return recurse(node, index);
215 } 215 }
216 216
217 @Override 217 @Override
218 public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { 218 public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) {
219 return recurse(node, index); 219 return recurse(node, index);
220 } 220 }
221 221
222 @Override 222 @Override
223 public Void visitText(TextNode node, SourceIndex index) { 223 public Void visitText(TextNode node, SourceIndex index) {
224 return recurse(node, index); 224 return recurse(node, index);
225 } 225 }
226 226
227 @Override 227 @Override
228 public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { 228 public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) {
229 return recurse(node, index); 229 return recurse(node, index);
230 } 230 }
231 231
232 @Override 232 @Override
233 public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { 233 public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) {
234 return recurse(node, index); 234 return recurse(node, index);
235 } 235 }
236 236
237 @Override 237 @Override
238 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { 238 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) {
239 return recurse(node, index); 239 return recurse(node, index);
240 } 240 }
241 241
242 @Override 242 @Override
243 public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { 243 public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) {
244 return recurse(node, index); 244 return recurse(node, index);
245 } 245 }
246 246
247 @Override 247 @Override
248 public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { 248 public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) {
249 return recurse(node, index); 249 return recurse(node, index);
250 } 250 }
251 251
252 @Override 252 @Override
253 public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { 253 public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) {
254 return recurse(node, index); 254 return recurse(node, index);
255 } 255 }
256 256
257 @Override 257 @Override
258 public Void visitComposedType(ComposedType node, SourceIndex index) { 258 public Void visitComposedType(ComposedType node, SourceIndex index) {
259 return recurse(node, index); 259 return recurse(node, index);
260 } 260 }
261 261
262 @Override 262 @Override
263 public Void visitWhileStatement(WhileStatement node, SourceIndex index) { 263 public Void visitWhileStatement(WhileStatement node, SourceIndex index) {
264 return recurse(node, index); 264 return recurse(node, index);
265 } 265 }
266 266
267 @Override 267 @Override
268 public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { 268 public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) {
269 return recurse(node, index); 269 return recurse(node, index);
270 } 270 }
271 271
272 @Override 272 @Override
273 public Void visitCastExpression(CastExpression node, SourceIndex index) { 273 public Void visitCastExpression(CastExpression node, SourceIndex index) {
274 return recurse(node, index); 274 return recurse(node, index);
275 } 275 }
276 276
277 @Override 277 @Override
278 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { 278 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) {
279 return recurse(node, index); 279 return recurse(node, index);
280 } 280 }
281 281
282 @Override 282 @Override
283 public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { 283 public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) {
284 return recurse(node, index); 284 return recurse(node, index);
285 } 285 }
286 286
287 @Override 287 @Override
288 public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { 288 public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) {
289 return recurse(node, index); 289 return recurse(node, index);
290 } 290 }
291 291
292 @Override 292 @Override
293 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { 293 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) {
294 return recurse(node, index); 294 return recurse(node, index);
295 } 295 }
296 296
297 @Override 297 @Override
298 public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { 298 public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) {
299 return recurse(node, index); 299 return recurse(node, index);
300 } 300 }
301 301
302 @Override 302 @Override
303 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { 303 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) {
304 return recurse(node, index); 304 return recurse(node, index);
305 } 305 }
306 306
307 @Override 307 @Override
308 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 308 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
309 return recurse(node, index); 309 return recurse(node, index);
310 } 310 }
311 311
312 @Override 312 @Override
313 public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { 313 public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) {
314 return recurse(node, index); 314 return recurse(node, index);
315 } 315 }
316 316
317 @Override 317 @Override
318 public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { 318 public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) {
319 return recurse(node, index); 319 return recurse(node, index);
320 } 320 }
321 321
322 @Override 322 @Override
323 public Void visitForStatement(ForStatement node, SourceIndex index) { 323 public Void visitForStatement(ForStatement node, SourceIndex index) {
324 return recurse(node, index); 324 return recurse(node, index);
325 } 325 }
326 326
327 @Override 327 @Override
328 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 328 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
329 return recurse(node, index); 329 return recurse(node, index);
330 } 330 }
331 331
332 @Override 332 @Override
333 public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { 333 public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) {
334 return recurse(node, index); 334 return recurse(node, index);
335 } 335 }
336 336
337 @Override 337 @Override
338 public Void visitGotoStatement(GotoStatement node, SourceIndex index) { 338 public Void visitGotoStatement(GotoStatement node, SourceIndex index) {
339 return recurse(node, index); 339 return recurse(node, index);
340 } 340 }
341 341
342 @Override 342 @Override
343 public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { 343 public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) {
344 return recurse(node, index); 344 return recurse(node, index);
345 } 345 }
346 346
347 @Override 347 @Override
348 public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { 348 public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) {
349 return recurse(node, index); 349 return recurse(node, index);
350 } 350 }
351 351
352 @Override 352 @Override
353 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { 353 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) {
354 return recurse(node, index); 354 return recurse(node, index);
355 } 355 }
356 356
357 @Override 357 @Override
358 public Void visitWildcardType(WildcardType node, SourceIndex index) { 358 public Void visitWildcardType(WildcardType node, SourceIndex index) {
359 return recurse(node, index); 359 return recurse(node, index);
360 } 360 }
361 361
362 @Override 362 @Override
363 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { 363 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) {
364 return recurse(node, index); 364 return recurse(node, index);
365 } 365 }
366 366
367 @Override 367 @Override
368 public Void visitAssertStatement(AssertStatement node, SourceIndex index) { 368 public Void visitAssertStatement(AssertStatement node, SourceIndex index) {
369 return recurse(node, index); 369 return recurse(node, index);
370 } 370 }
371 371
372 @Override 372 @Override
373 public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { 373 public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) {
374 return recurse(node, index); 374 return recurse(node, index);
375 } 375 }
376 376
377 @Override 377 @Override
378 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { 378 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) {
379 return recurse(node, index); 379 return recurse(node, index);
380 } 380 }
381} 381}
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 42f4660..266d202 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -8,48 +8,48 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13public class Token implements Comparable<Token> { 14public class Token implements Comparable<Token> {
14 15
15 public int start; 16 public int start;
16 public int end; 17 public int end;
17 public String text; 18 public String text;
18 19
19 public Token(int start, int end, String source) { 20 public Token(int start, int end, String source) {
20 this.start = start; 21 this.start = start;
21 this.end = end; 22 this.end = end;
22 if (source != null) { 23 if (source != null) {
23 this.text = source.substring(start, end); 24 this.text = source.substring(start, end);
24 } 25 }
25 } 26 }
26 27
27 public boolean contains(int pos) { 28 public boolean contains(int pos) {
28 return pos >= start && pos <= end; 29 return pos >= start && pos <= end;
29 } 30 }
30 31
31 @Override 32 @Override
32 public int compareTo(Token other) { 33 public int compareTo(Token other) {
33 return start - other.start; 34 return start - other.start;
34 } 35 }
35 36
36 @Override 37 @Override
37 public boolean equals(Object other) { 38 public boolean equals(Object other) {
38 return other instanceof Token && equals((Token) other); 39 return other instanceof Token && equals((Token) other);
39 } 40 }
40 41
41 @Override 42 @Override
42 public int hashCode() 43 public int hashCode() {
43 { 44 return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0);
44 return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); 45 }
45 } 46
46 47 public boolean equals(Token other) {
47 public boolean equals(Token other) { 48 return start == other.start && end == other.end;
48 return start == other.start && end == other.end; 49 }
49 } 50
50 51 @Override
51 @Override 52 public String toString() {
52 public String toString() { 53 return String.format("[%d,%d]", start, end);
53 return String.format("[%d,%d]", start, end); 54 }
54 }
55} 55}
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index d51131f..26be05b 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -8,291 +8,288 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
17
18import java.util.Collection;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import cuchaz.enigma.mapping.*; 18import cuchaz.enigma.mapping.*;
24import javassist.CtBehavior; 19import javassist.CtBehavior;
25import javassist.CtClass; 20import javassist.CtClass;
26import javassist.CtField; 21import javassist.CtField;
27import javassist.bytecode.Descriptor; 22import javassist.bytecode.Descriptor;
28 23
24import java.util.Collection;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28
29public class TranslationIndex { 29public class TranslationIndex {
30 30
31 private Map<ClassEntry, ClassEntry> superclasses; 31 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 32 private Multimap<ClassEntry, FieldEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 34 private Multimap<ClassEntry, ClassEntry> interfaces;
35 35
36 public TranslationIndex() { 36 public TranslationIndex() {
37 this.superclasses = Maps.newHashMap(); 37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 38 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 39 this.behaviorEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 40 this.interfaces = HashMultimap.create();
41 } 41 }
42 42
43 public TranslationIndex(TranslationIndex other, Translator translator) { 43 public TranslationIndex(TranslationIndex other, Translator translator) {
44 // translate the superclasses 44 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 45 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue()));
48 } 48 }
49 49
50 // translate the interfaces 50 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 51 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 53 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 54 translator.translateEntry(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 55 translator.translateEntry(mapEntry.getValue())
56 ); 56 );
57 } 57 }
58 58
59 // translate the fields 59 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 60 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 62 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 63 translator.translateEntry(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 64 translator.translateEntry(mapEntry.getValue())
65 ); 65 );
66 } 66 }
67 67
68 this.behaviorEntries = HashMultimap.create(); 68 this.behaviorEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) {
70 this.behaviorEntries.put( 70 this.behaviorEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 71 translator.translateEntry(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 72 translator.translateEntry(mapEntry.getValue())
73 ); 73 );
74 } 74 }
75 } 75 }
76 76
77 public void indexClass(CtClass c) { 77 public void indexClass(CtClass c) {
78 indexClass(c, true); 78 indexClass(c, true);
79 } 79 }
80 80
81 public void indexClass(CtClass c, boolean indexMembers) { 81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c); 82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 83 if (isJre(classEntry)) {
84 return; 84 return;
85 } 85 }
86 86
87 // add the superclass 87 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c);
89 if (superclassEntry != null) { 89 if (superclassEntry != null) {
90 this.superclasses.put(classEntry, superclassEntry); 90 this.superclasses.put(classEntry, superclassEntry);
91 } 91 }
92 92
93 // add the interfaces 93 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 94 for (String interfaceClassName : c.getClassFile().getInterfaces()) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName));
96 if (!isJre(interfaceClassEntry)) { 96 if (!isJre(interfaceClassEntry)) {
97 97
98 this.interfaces.put(classEntry, interfaceClassEntry); 98 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 99 }
100 } 100 }
101 101
102 if (indexMembers) { 102 if (indexMembers) {
103 // add fields 103 // add fields
104 for (CtField field : c.getDeclaredFields()) { 104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); 106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 } 107 }
108 108
109 // add behaviors 109 // add behaviors
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 110 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
113 } 113 }
114 } 114 }
115 } 115 }
116 116
117 public void renameClasses(Map<String, String> renames) { 117 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 118 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries);
121 } 121 }
122 122
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 123 public ClassEntry getSuperclass(ClassEntry classEntry) {
124 return this.superclasses.get(classEntry); 124 return this.superclasses.get(classEntry);
125 } 125 }
126 126
127 public List<ClassEntry> getAncestry(ClassEntry classEntry) { 127 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
128 List<ClassEntry> ancestors = Lists.newArrayList(); 128 List<ClassEntry> ancestors = Lists.newArrayList();
129 while (classEntry != null) { 129 while (classEntry != null) {
130 classEntry = getSuperclass(classEntry); 130 classEntry = getSuperclass(classEntry);
131 if (classEntry != null) { 131 if (classEntry != null) {
132 ancestors.add(classEntry); 132 ancestors.add(classEntry);
133 } 133 }
134 } 134 }
135 return ancestors; 135 return ancestors;
136 } 136 }
137 137
138 public List<ClassEntry> getSubclass(ClassEntry classEntry) { 138 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
139 // linear search is fast enough for now 139 // linear search is fast enough for now
140 List<ClassEntry> subclasses = Lists.newArrayList(); 140 List<ClassEntry> subclasses = Lists.newArrayList();
141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { 141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
142 ClassEntry subclass = entry.getKey(); 142 ClassEntry subclass = entry.getKey();
143 ClassEntry superclass = entry.getValue(); 143 ClassEntry superclass = entry.getValue();
144 if (classEntry.equals(superclass)) { 144 if (classEntry.equals(superclass)) {
145 subclasses.add(subclass); 145 subclasses.add(subclass);
146 } 146 }
147 } 147 }
148 return subclasses; 148 return subclasses;
149 } 149 }
150 150
151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { 151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
152 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 152 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
153 out.add(subclassEntry); 153 out.add(subclassEntry);
154 getSubclassesRecursively(out, subclassEntry); 154 getSubclassesRecursively(out, subclassEntry);
155 } 155 }
156 } 156 }
157 157
158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { 158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
159 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 159 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
160 out.add(subclassEntry.getName()); 160 out.add(subclassEntry.getName());
161 getSubclassNamesRecursively(out, subclassEntry); 161 getSubclassNamesRecursively(out, subclassEntry);
162 } 162 }
163 } 163 }
164 164
165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { 165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() {
166 return this.interfaces.entries(); 166 return this.interfaces.entries();
167 } 167 }
168 168
169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { 169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
170 return this.interfaces.get(classEntry); 170 return this.interfaces.get(classEntry);
171 } 171 }
172 172
173 public boolean isInterface(ClassEntry classEntry) { 173 public boolean isInterface(ClassEntry classEntry) {
174 return this.interfaces.containsValue(classEntry); 174 return this.interfaces.containsValue(classEntry);
175 } 175 }
176 176
177 public boolean entryExists(Entry entry) { 177 public boolean entryExists(Entry entry) {
178 if (entry instanceof FieldEntry) { 178 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 179 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 180 } else if (entry instanceof BehaviorEntry) {
181 return behaviorExists((BehaviorEntry) entry); 181 return behaviorExists((BehaviorEntry) entry);
182 } else if (entry instanceof ArgumentEntry) { 182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); 183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 184 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry());
186 } 186 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 188 }
189 189
190 public boolean fieldExists(FieldEntry fieldEntry) { 190 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
192 } 192 }
193 193
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 194 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
196 } 196 }
197 197
198 public ClassEntry resolveEntryClass(Entry entry) { 198 public ClassEntry resolveEntryClass(Entry entry) {
199 return resolveEntryClass(entry, false); 199 return resolveEntryClass(entry, false);
200 } 200 }
201 201
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 203 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 204 return (ClassEntry) entry;
205 } 205 }
206 206
207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); 207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild);
208 if (superclassEntry != null) { 208 if (superclassEntry != null) {
209 return superclassEntry; 209 return superclassEntry;
210 } 210 }
211 211
212 ClassEntry interfaceEntry = resolveInterface(entry); 212 ClassEntry interfaceEntry = resolveInterface(entry);
213 if (interfaceEntry != null) { 213 if (interfaceEntry != null) {
214 return interfaceEntry; 214 return interfaceEntry;
215 } 215 }
216 216
217 return null; 217 return null;
218 } 218 }
219 219
220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { 220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) {
221 221
222 // Default case 222 // Default case
223 if (!checkSuperclassBeforeChild) 223 if (!checkSuperclassBeforeChild)
224 return resolveSuperclass(entry); 224 return resolveSuperclass(entry);
225 225
226 // Save the original entry 226 // Save the original entry
227 Entry originalEntry = entry; 227 Entry originalEntry = entry;
228 228
229 // Get all possible superclasses and reverse the list 229 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry()));
231 231
232 boolean existInEntry = false; 232 boolean existInEntry = false;
233 233
234 for (ClassEntry classEntry : superclasses) 234 for (ClassEntry classEntry : superclasses) {
235 { 235 entry = entry.cloneToNewClass(classEntry);
236 entry = entry.cloneToNewClass(classEntry); 236 existInEntry = entryExists(entry);
237 existInEntry = entryExists(entry); 237
238 238 // Check for possible entry in interfaces of superclasses
239 // Check for possible entry in interfaces of superclasses 239 ClassEntry interfaceEntry = resolveInterface(entry);
240 ClassEntry interfaceEntry = resolveInterface(entry); 240 if (interfaceEntry != null)
241 if (interfaceEntry != null) 241 return interfaceEntry;
242 return interfaceEntry; 242 if (existInEntry)
243 if (existInEntry) 243 break;
244 break; 244 }
245 } 245
246 246 // Doesn't exists in superclasses? check the child or return null
247 // Doesn't exists in superclasses? check the child or return null 247 if (!existInEntry)
248 if (!existInEntry) 248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry();
249 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 249
250 250 return entry.getClassEntry();
251 return entry.getClassEntry(); 251 }
252 } 252
253 253 public ClassEntry resolveSuperclass(Entry entry) {
254 public ClassEntry resolveSuperclass(Entry entry) 254 // this entry could refer to a method on a class where the method is not actually implemented
255 { 255 // travel up the inheritance tree to find the closest implementation
256 // this entry could refer to a method on a class where the method is not actually implemented 256
257 // travel up the inheritance tree to find the closest implementation 257 while (!entryExists(entry)) {
258 258 // is there a parent class?
259 while (!entryExists(entry)) { 259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
260 // is there a parent class? 260 if (superclassEntry == null) {
261 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 261 // this is probably a method from a class in a library
262 if (superclassEntry == null) { 262 // we can't trace the implementation up any higher unless we index the library
263 // this is probably a method from a class in a library 263 return null;
264 // we can't trace the implementation up any higher unless we index the library 264 }
265 return null; 265
266 } 266 // move up to the parent class
267 267 entry = entry.cloneToNewClass(superclassEntry);
268 // move up to the parent class 268 }
269 entry = entry.cloneToNewClass(superclassEntry); 269 return entry.getClassEntry();
270 } 270 }
271 return entry.getClassEntry(); 271
272 } 272 public ClassEntry resolveInterface(Entry entry) {
273 273 // the interfaces for any class is a forest
274 public ClassEntry resolveInterface(Entry entry) { 274 // so let's look at all the trees
275 // the interfaces for any class is a forest 275
276 // so let's look at all the trees 276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) {
277 277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 278 if (subInterface != null && !subInterface.isEmpty()) {
279 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry));
280 if (subInterface != null && !subInterface.isEmpty()) 280 if (result != null)
281 { 281 return result;
282 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 282 }
283 if (result != null) 283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry));
284 return result; 284 if (resolvedClassEntry != null) {
285 } 285 return resolvedClassEntry;
286 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 286 }
287 if (resolvedClassEntry != null) { 287 }
288 return resolvedClassEntry; 288 return null;
289 } 289 }
290 } 290
291 return null; 291 private boolean isJre(ClassEntry classEntry) {
292 } 292 String packageName = classEntry.getPackageName();
293 293 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
294 private boolean isJre(ClassEntry classEntry) { 294 }
295 String packageName = classEntry.getPackageName();
296 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
297 }
298} 295}
diff --git a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
index cc025da..c98fb9e 100644
--- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java
@@ -8,6 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.strobel.componentmodel.Key; 14import com.strobel.componentmodel.Key;
@@ -19,420 +20,420 @@ import java.nio.charset.Charset;
19 20
20public class TreeDumpVisitor implements IAstVisitor<Void, Void> { 21public class TreeDumpVisitor implements IAstVisitor<Void, Void> {
21 22
22 private File file; 23 private File file;
23 private Writer out; 24 private Writer out;
24 25
25 public TreeDumpVisitor(File file) { 26 public TreeDumpVisitor(File file) {
26 this.file = file; 27 this.file = file;
27 } 28 }
28 29
29 @Override 30 @Override
30 public Void visitCompilationUnit(CompilationUnit node, Void ignored) { 31 public Void visitCompilationUnit(CompilationUnit node, Void ignored) {
31 try { 32 try {
32 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); 33 out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
33 recurse(node, ignored); 34 recurse(node, ignored);
34 out.close(); 35 out.close();
35 return null; 36 return null;
36 } catch (IOException ex) { 37 } catch (IOException ex) {
37 throw new Error(ex); 38 throw new Error(ex);
38 } 39 }
39 } 40 }
40 41
41 private Void recurse(AstNode node, Void ignored) { 42 private Void recurse(AstNode node, Void ignored) {
42 // show the tree 43 // show the tree
43 try { 44 try {
44 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); 45 out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n");
45 } catch (IOException ex) { 46 } catch (IOException ex) {
46 throw new Error(ex); 47 throw new Error(ex);
47 } 48 }
48 49
49 // recurse 50 // recurse
50 for (final AstNode child : node.getChildren()) { 51 for (final AstNode child : node.getChildren()) {
51 child.acceptVisitor(this, ignored); 52 child.acceptVisitor(this, ignored);
52 } 53 }
53 return null; 54 return null;
54 } 55 }
55 56
56 private String getText(AstNode node) { 57 private String getText(AstNode node) {
57 if (node instanceof Identifier) { 58 if (node instanceof Identifier) {
58 return "\"" + ((Identifier) node).getName() + "\""; 59 return "\"" + ((Identifier) node).getName() + "\"";
59 } 60 }
60 return ""; 61 return "";
61 } 62 }
62 63
63 private String dumpUserData(AstNode node) { 64 private String dumpUserData(AstNode node) {
64 StringBuilder buf = new StringBuilder(); 65 StringBuilder buf = new StringBuilder();
65 for (Key<?> key : Keys.ALL_KEYS) { 66 for (Key<?> key : Keys.ALL_KEYS) {
66 Object val = node.getUserData(key); 67 Object val = node.getUserData(key);
67 if (val != null) { 68 if (val != null) {
68 buf.append(String.format(" [%s=%s]", key, val)); 69 buf.append(String.format(" [%s=%s]", key, val));
69 } 70 }
70 } 71 }
71 return buf.toString(); 72 return buf.toString();
72 } 73 }
73 74
74 private String getIndent(AstNode node) { 75 private String getIndent(AstNode node) {
75 StringBuilder buf = new StringBuilder(); 76 StringBuilder buf = new StringBuilder();
76 int depth = getDepth(node); 77 int depth = getDepth(node);
77 for (int i = 0; i < depth; i++) { 78 for (int i = 0; i < depth; i++) {
78 buf.append("\t"); 79 buf.append("\t");
79 } 80 }
80 return buf.toString(); 81 return buf.toString();
81 } 82 }
82 83
83 private int getDepth(AstNode node) { 84 private int getDepth(AstNode node) {
84 int depth = -1; 85 int depth = -1;
85 while (node != null) { 86 while (node != null) {
86 depth++; 87 depth++;
87 node = node.getParent(); 88 node = node.getParent();
88 } 89 }
89 return depth; 90 return depth;
90 } 91 }
91 92
92 // OVERRIDES WE DON'T CARE ABOUT 93 // OVERRIDES WE DON'T CARE ABOUT
93 94
94 @Override 95 @Override
95 public Void visitInvocationExpression(InvocationExpression node, Void ignored) { 96 public Void visitInvocationExpression(InvocationExpression node, Void ignored) {
96 return recurse(node, ignored); 97 return recurse(node, ignored);
97 } 98 }
98 99
99 @Override 100 @Override
100 public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { 101 public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) {
101 return recurse(node, ignored); 102 return recurse(node, ignored);
102 } 103 }
103 104
104 @Override 105 @Override
105 public Void visitSimpleType(SimpleType node, Void ignored) { 106 public Void visitSimpleType(SimpleType node, Void ignored) {
106 return recurse(node, ignored); 107 return recurse(node, ignored);
107 } 108 }
108 109
109 @Override 110 @Override
110 public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { 111 public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) {
111 return recurse(node, ignored); 112 return recurse(node, ignored);
112 } 113 }
113 114
114 @Override 115 @Override
115 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { 116 public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) {
116 return recurse(node, ignored); 117 return recurse(node, ignored);
117 } 118 }
118 119
119 @Override 120 @Override
120 public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { 121 public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) {
121 return recurse(node, ignored); 122 return recurse(node, ignored);
122 } 123 }
123 124
124 @Override 125 @Override
125 public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { 126 public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) {
126 return recurse(node, ignored); 127 return recurse(node, ignored);
127 } 128 }
128 129
129 @Override 130 @Override
130 public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { 131 public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) {
131 return recurse(node, ignored); 132 return recurse(node, ignored);
132 } 133 }
133 134
134 @Override 135 @Override
135 public Void visitComment(Comment node, Void ignored) { 136 public Void visitComment(Comment node, Void ignored) {
136 return recurse(node, ignored); 137 return recurse(node, ignored);
137 } 138 }
138 139
139 @Override 140 @Override
140 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { 141 public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) {
141 return recurse(node, ignored); 142 return recurse(node, ignored);
142 } 143 }
143 144
144 @Override 145 @Override
145 public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { 146 public Void visitTypeReference(TypeReferenceExpression node, Void ignored) {
146 return recurse(node, ignored); 147 return recurse(node, ignored);
147 } 148 }
148 149
149 @Override 150 @Override
150 public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { 151 public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) {
151 return recurse(node, ignored); 152 return recurse(node, ignored);
152 } 153 }
153 154
154 @Override 155 @Override
155 public Void visitIdentifier(Identifier node, Void ignored) { 156 public Void visitIdentifier(Identifier node, Void ignored) {
156 return recurse(node, ignored); 157 return recurse(node, ignored);
157 } 158 }
158 159
159 @Override 160 @Override
160 public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { 161 public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) {
161 return recurse(node, ignored); 162 return recurse(node, ignored);
162 } 163 }
163 164
164 @Override 165 @Override
165 public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { 166 public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) {
166 return recurse(node, ignored); 167 return recurse(node, ignored);
167 } 168 }
168 169
169 @Override 170 @Override
170 public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { 171 public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) {
171 return recurse(node, ignored); 172 return recurse(node, ignored);
172 } 173 }
173 174
174 @Override 175 @Override
175 public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { 176 public Void visitClassOfExpression(ClassOfExpression node, Void ignored) {
176 return recurse(node, ignored); 177 return recurse(node, ignored);
177 } 178 }
178 179
179 @Override 180 @Override
180 public Void visitBlockStatement(BlockStatement node, Void ignored) { 181 public Void visitBlockStatement(BlockStatement node, Void ignored) {
181 return recurse(node, ignored); 182 return recurse(node, ignored);
182 } 183 }
183 184
184 @Override 185 @Override
185 public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { 186 public Void visitExpressionStatement(ExpressionStatement node, Void ignored) {
186 return recurse(node, ignored); 187 return recurse(node, ignored);
187 } 188 }
188 189
189 @Override 190 @Override
190 public Void visitBreakStatement(BreakStatement node, Void ignored) { 191 public Void visitBreakStatement(BreakStatement node, Void ignored) {
191 return recurse(node, ignored); 192 return recurse(node, ignored);
192 } 193 }
193 194
194 @Override 195 @Override
195 public Void visitContinueStatement(ContinueStatement node, Void ignored) { 196 public Void visitContinueStatement(ContinueStatement node, Void ignored) {
196 return recurse(node, ignored); 197 return recurse(node, ignored);
197 } 198 }
198 199
199 @Override 200 @Override
200 public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { 201 public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) {
201 return recurse(node, ignored); 202 return recurse(node, ignored);
202 } 203 }
203 204
204 @Override 205 @Override
205 public Void visitEmptyStatement(EmptyStatement node, Void ignored) { 206 public Void visitEmptyStatement(EmptyStatement node, Void ignored) {
206 return recurse(node, ignored); 207 return recurse(node, ignored);
207 } 208 }
208 209
209 @Override 210 @Override
210 public Void visitIfElseStatement(IfElseStatement node, Void ignored) { 211 public Void visitIfElseStatement(IfElseStatement node, Void ignored) {
211 return recurse(node, ignored); 212 return recurse(node, ignored);
212 } 213 }
213 214
214 @Override 215 @Override
215 public Void visitLabelStatement(LabelStatement node, Void ignored) { 216 public Void visitLabelStatement(LabelStatement node, Void ignored) {
216 return recurse(node, ignored); 217 return recurse(node, ignored);
217 } 218 }
218 219
219 @Override 220 @Override
220 public Void visitLabeledStatement(LabeledStatement node, Void ignored) { 221 public Void visitLabeledStatement(LabeledStatement node, Void ignored) {
221 return recurse(node, ignored); 222 return recurse(node, ignored);
222 } 223 }
223 224
224 @Override 225 @Override
225 public Void visitReturnStatement(ReturnStatement node, Void ignored) { 226 public Void visitReturnStatement(ReturnStatement node, Void ignored) {
226 return recurse(node, ignored); 227 return recurse(node, ignored);
227 } 228 }
228 229
229 @Override 230 @Override
230 public Void visitSwitchStatement(SwitchStatement node, Void ignored) { 231 public Void visitSwitchStatement(SwitchStatement node, Void ignored) {
231 return recurse(node, ignored); 232 return recurse(node, ignored);
232 } 233 }
233 234
234 @Override 235 @Override
235 public Void visitSwitchSection(SwitchSection node, Void ignored) { 236 public Void visitSwitchSection(SwitchSection node, Void ignored) {
236 return recurse(node, ignored); 237 return recurse(node, ignored);
237 } 238 }
238 239
239 @Override 240 @Override
240 public Void visitCaseLabel(CaseLabel node, Void ignored) { 241 public Void visitCaseLabel(CaseLabel node, Void ignored) {
241 return recurse(node, ignored); 242 return recurse(node, ignored);
242 } 243 }
243 244
244 @Override 245 @Override
245 public Void visitThrowStatement(ThrowStatement node, Void ignored) { 246 public Void visitThrowStatement(ThrowStatement node, Void ignored) {
246 return recurse(node, ignored); 247 return recurse(node, ignored);
247 } 248 }
248 249
249 @Override 250 @Override
250 public Void visitCatchClause(CatchClause node, Void ignored) { 251 public Void visitCatchClause(CatchClause node, Void ignored) {
251 return recurse(node, ignored); 252 return recurse(node, ignored);
252 } 253 }
253 254
254 @Override 255 @Override
255 public Void visitAnnotation(Annotation node, Void ignored) { 256 public Void visitAnnotation(Annotation node, Void ignored) {
256 return recurse(node, ignored); 257 return recurse(node, ignored);
257 } 258 }
258 259
259 @Override 260 @Override
260 public Void visitNewLine(NewLineNode node, Void ignored) { 261 public Void visitNewLine(NewLineNode node, Void ignored) {
261 return recurse(node, ignored); 262 return recurse(node, ignored);
262 } 263 }
263 264
264 @Override 265 @Override
265 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { 266 public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) {
266 return recurse(node, ignored); 267 return recurse(node, ignored);
267 } 268 }
268 269
269 @Override 270 @Override
270 public Void visitVariableInitializer(VariableInitializer node, Void ignored) { 271 public Void visitVariableInitializer(VariableInitializer node, Void ignored) {
271 return recurse(node, ignored); 272 return recurse(node, ignored);
272 } 273 }
273 274
274 @Override 275 @Override
275 public Void visitText(TextNode node, Void ignored) { 276 public Void visitText(TextNode node, Void ignored) {
276 return recurse(node, ignored); 277 return recurse(node, ignored);
277 } 278 }
278 279
279 @Override 280 @Override
280 public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { 281 public Void visitImportDeclaration(ImportDeclaration node, Void ignored) {
281 return recurse(node, ignored); 282 return recurse(node, ignored);
282 } 283 }
283 284
284 @Override 285 @Override
285 public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { 286 public Void visitInitializerBlock(InstanceInitializer node, Void ignored) {
286 return recurse(node, ignored); 287 return recurse(node, ignored);
287 } 288 }
288 289
289 @Override 290 @Override
290 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { 291 public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) {
291 return recurse(node, ignored); 292 return recurse(node, ignored);
292 } 293 }
293 294
294 @Override 295 @Override
295 public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { 296 public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) {
296 return recurse(node, ignored); 297 return recurse(node, ignored);
297 } 298 }
298 299
299 @Override 300 @Override
300 public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { 301 public Void visitArraySpecifier(ArraySpecifier node, Void ignored) {
301 return recurse(node, ignored); 302 return recurse(node, ignored);
302 } 303 }
303 304
304 @Override 305 @Override
305 public Void visitComposedType(ComposedType node, Void ignored) { 306 public Void visitComposedType(ComposedType node, Void ignored) {
306 return recurse(node, ignored); 307 return recurse(node, ignored);
307 } 308 }
308 309
309 @Override 310 @Override
310 public Void visitWhileStatement(WhileStatement node, Void ignored) { 311 public Void visitWhileStatement(WhileStatement node, Void ignored) {
311 return recurse(node, ignored); 312 return recurse(node, ignored);
312 } 313 }
313 314
314 @Override 315 @Override
315 public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { 316 public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) {
316 return recurse(node, ignored); 317 return recurse(node, ignored);
317 } 318 }
318 319
319 @Override 320 @Override
320 public Void visitCastExpression(CastExpression node, Void ignored) { 321 public Void visitCastExpression(CastExpression node, Void ignored) {
321 return recurse(node, ignored); 322 return recurse(node, ignored);
322 } 323 }
323 324
324 @Override 325 @Override
325 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { 326 public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) {
326 return recurse(node, ignored); 327 return recurse(node, ignored);
327 } 328 }
328 329
329 @Override 330 @Override
330 public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { 331 public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) {
331 return recurse(node, ignored); 332 return recurse(node, ignored);
332 } 333 }
333 334
334 @Override 335 @Override
335 public Void visitIndexerExpression(IndexerExpression node, Void ignored) { 336 public Void visitIndexerExpression(IndexerExpression node, Void ignored) {
336 return recurse(node, ignored); 337 return recurse(node, ignored);
337 } 338 }
338 339
339 @Override 340 @Override
340 public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { 341 public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) {
341 return recurse(node, ignored); 342 return recurse(node, ignored);
342 } 343 }
343 344
344 @Override 345 @Override
345 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { 346 public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) {
346 return recurse(node, ignored); 347 return recurse(node, ignored);
347 } 348 }
348 349
349 @Override 350 @Override
350 public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { 351 public Void visitConditionalExpression(ConditionalExpression node, Void ignored) {
351 return recurse(node, ignored); 352 return recurse(node, ignored);
352 } 353 }
353 354
354 @Override 355 @Override
355 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { 356 public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) {
356 return recurse(node, ignored); 357 return recurse(node, ignored);
357 } 358 }
358 359
359 @Override 360 @Override
360 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { 361 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) {
361 return recurse(node, ignored); 362 return recurse(node, ignored);
362 } 363 }
363 364
364 @Override 365 @Override
365 public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { 366 public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) {
366 return recurse(node, ignored); 367 return recurse(node, ignored);
367 } 368 }
368 369
369 @Override 370 @Override
370 public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { 371 public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) {
371 return recurse(node, ignored); 372 return recurse(node, ignored);
372 } 373 }
373 374
374 @Override 375 @Override
375 public Void visitForStatement(ForStatement node, Void ignored) { 376 public Void visitForStatement(ForStatement node, Void ignored) {
376 return recurse(node, ignored); 377 return recurse(node, ignored);
377 } 378 }
378 379
379 @Override 380 @Override
380 public Void visitForEachStatement(ForEachStatement node, Void ignored) { 381 public Void visitForEachStatement(ForEachStatement node, Void ignored) {
381 return recurse(node, ignored); 382 return recurse(node, ignored);
382 } 383 }
383 384
384 @Override 385 @Override
385 public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { 386 public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) {
386 return recurse(node, ignored); 387 return recurse(node, ignored);
387 } 388 }
388 389
389 @Override 390 @Override
390 public Void visitGotoStatement(GotoStatement node, Void ignored) { 391 public Void visitGotoStatement(GotoStatement node, Void ignored) {
391 return recurse(node, ignored); 392 return recurse(node, ignored);
392 } 393 }
393 394
394 @Override 395 @Override
395 public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { 396 public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) {
396 return recurse(node, ignored); 397 return recurse(node, ignored);
397 } 398 }
398 399
399 @Override 400 @Override
400 public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { 401 public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) {
401 return recurse(node, ignored); 402 return recurse(node, ignored);
402 } 403 }
403 404
404 @Override 405 @Override
405 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { 406 public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) {
406 return recurse(node, ignored); 407 return recurse(node, ignored);
407 } 408 }
408 409
409 @Override 410 @Override
410 public Void visitWildcardType(WildcardType node, Void ignored) { 411 public Void visitWildcardType(WildcardType node, Void ignored) {
411 return recurse(node, ignored); 412 return recurse(node, ignored);
412 } 413 }
413 414
414 @Override 415 @Override
415 public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { 416 public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) {
416 return recurse(node, ignored); 417 return recurse(node, ignored);
417 } 418 }
418 419
419 @Override 420 @Override
420 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { 421 public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) {
421 return recurse(node, ignored); 422 return recurse(node, ignored);
422 } 423 }
423 424
424 @Override 425 @Override
425 public Void visitAssertStatement(AssertStatement node, Void ignored) { 426 public Void visitAssertStatement(AssertStatement node, Void ignored) {
426 return recurse(node, ignored); 427 return recurse(node, ignored);
427 } 428 }
428 429
429 @Override 430 @Override
430 public Void visitLambdaExpression(LambdaExpression node, Void ignored) { 431 public Void visitLambdaExpression(LambdaExpression node, Void ignored) {
431 return recurse(node, ignored); 432 return recurse(node, ignored);
432 } 433 }
433 434
434 @Override 435 @Override
435 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { 436 public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) {
436 return recurse(node, ignored); 437 return recurse(node, ignored);
437 } 438 }
438} 439}