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