summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar jeff2015-02-08 21:29:25 -0500
committerGravatar jeff2015-02-08 21:29:25 -0500
commited9b5cdfc648e86fd463bfa8d86b94c41671e14c (patch)
tree2619bbc7e04dfa3b82f8dfd3b1d31f529766cd4b /src/cuchaz/enigma/analysis
downloadenigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.tar.gz
enigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.tar.xz
enigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.zip
switch all classes to new signature/type system
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/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.java171
-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.java734
-rw-r--r--src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java100
-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/SourceIndex.java173
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java164
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java115
-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.java227
-rw-r--r--src/cuchaz/enigma/analysis/TreeDumpVisitor.java512
19 files changed, 3481 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..8d3409a
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/Access.java
@@ -0,0 +1,43 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..9adac5e
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
@@ -0,0 +1,93 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
new file mode 100644
index 0000000..49aac5f
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -0,0 +1,80 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..3eaa391
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -0,0 +1,85 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..bb611df
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/EntryReference.java
@@ -0,0 +1,126 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.getInnerClassName();
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..b54489c
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/EntryRenamer.java
@@ -0,0 +1,171 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.ConstructorEntry;
25import cuchaz.enigma.mapping.Entry;
26import cuchaz.enigma.mapping.FieldEntry;
27import cuchaz.enigma.mapping.MethodEntry;
28
29public class EntryRenamer {
30
31 public static <T> void renameClassesInSet(Map<String,String> renames, Set<T> set) {
32 List<T> entries = Lists.newArrayList();
33 for (T val : set) {
34 entries.add(renameClassesInThing(renames, val));
35 }
36 set.clear();
37 set.addAll(entries);
38 }
39
40 public static <Key,Val> void renameClassesInMap(Map<String,String> renames, Map<Key,Val> map) {
41 // for each key/value pair...
42 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
43 for (Map.Entry<Key,Val> entry : map.entrySet()) {
44 entriesToAdd.add(new AbstractMap.SimpleEntry<Key,Val>(
45 renameClassesInThing(renames, entry.getKey()),
46 renameClassesInThing(renames, entry.getValue())
47 ));
48 }
49 map.clear();
50 for (Map.Entry<Key,Val> entry : entriesToAdd) {
51 map.put(entry.getKey(), entry.getValue());
52 }
53 }
54
55 public static <Key,Val> void renameClassesInMultimap(Map<String,String> renames, Multimap<Key,Val> map) {
56 // for each key/value pair...
57 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
58 for (Map.Entry<Key,Val> entry : map.entries()) {
59 entriesToAdd.add(new AbstractMap.SimpleEntry<Key,Val>(
60 renameClassesInThing(renames, entry.getKey()),
61 renameClassesInThing(renames, entry.getValue())
62 ));
63 }
64 map.clear();
65 for (Map.Entry<Key,Val> entry : entriesToAdd) {
66 map.put(entry.getKey(), entry.getValue());
67 }
68 }
69
70 public static <Key,Val> void renameMethodsInMultimap(Map<MethodEntry,MethodEntry> renames, Multimap<Key,Val> map) {
71 // for each key/value pair...
72 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
73 for (Map.Entry<Key,Val> entry : map.entries()) {
74 entriesToAdd.add(new AbstractMap.SimpleEntry<Key,Val>(
75 renameMethodsInThing(renames, entry.getKey()),
76 renameMethodsInThing(renames, entry.getValue())
77 ));
78 }
79 map.clear();
80 for (Map.Entry<Key,Val> entry : entriesToAdd) {
81 map.put(entry.getKey(), entry.getValue());
82 }
83 }
84
85 public static <Key,Val> void renameMethodsInMap(Map<MethodEntry,MethodEntry> renames, Map<Key,Val> map) {
86 // for each key/value pair...
87 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
88 for (Map.Entry<Key,Val> entry : map.entrySet()) {
89 entriesToAdd.add(new AbstractMap.SimpleEntry<Key,Val>(
90 renameMethodsInThing(renames, entry.getKey()),
91 renameMethodsInThing(renames, entry.getValue())
92 ));
93 }
94 map.clear();
95 for (Map.Entry<Key,Val> entry : entriesToAdd) {
96 map.put(entry.getKey(), entry.getValue());
97 }
98 }
99
100 @SuppressWarnings("unchecked")
101 public static <T> T renameMethodsInThing(Map<MethodEntry,MethodEntry> renames, T thing) {
102 if (thing instanceof MethodEntry) {
103 MethodEntry methodEntry = (MethodEntry)thing;
104 MethodEntry newMethodEntry = renames.get(methodEntry);
105 if (newMethodEntry != null) {
106 return (T)new MethodEntry(
107 methodEntry.getClassEntry(),
108 newMethodEntry.getName(),
109 methodEntry.getSignature()
110 );
111 }
112 return thing;
113 } else if (thing instanceof ArgumentEntry) {
114 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
115 return (T)new ArgumentEntry(
116 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()),
117 argumentEntry.getIndex(),
118 argumentEntry.getName()
119 );
120 } else if (thing instanceof EntryReference) {
121 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
122 reference.entry = renameMethodsInThing(renames, reference.entry);
123 reference.context = renameMethodsInThing(renames, reference.context);
124 return thing;
125 }
126 return thing;
127 }
128
129 @SuppressWarnings("unchecked")
130 public static <T> T renameClassesInThing(Map<String,String> renames, T thing) {
131 if (thing instanceof String) {
132 String stringEntry = (String)thing;
133 if (renames.containsKey(stringEntry)) {
134 return (T)renames.get(stringEntry);
135 }
136 } else if (thing instanceof ClassEntry) {
137 ClassEntry classEntry = (ClassEntry)thing;
138 return (T)new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
139 } else if (thing instanceof FieldEntry) {
140 FieldEntry fieldEntry = (FieldEntry)thing;
141 return (T)new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName());
142 } else if (thing instanceof ConstructorEntry) {
143 ConstructorEntry constructorEntry = (ConstructorEntry)thing;
144 return (T)new ConstructorEntry(
145 renameClassesInThing(renames, constructorEntry.getClassEntry()),
146 constructorEntry.getSignature()
147 );
148 } else if (thing instanceof MethodEntry) {
149 MethodEntry methodEntry = (MethodEntry)thing;
150 return (T)new MethodEntry(
151 renameClassesInThing(renames, methodEntry.getClassEntry()),
152 methodEntry.getName(),
153 methodEntry.getSignature()
154 );
155 } else if (thing instanceof ArgumentEntry) {
156 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
157 return (T)new ArgumentEntry(
158 renameClassesInThing(renames, argumentEntry.getBehaviorEntry()),
159 argumentEntry.getIndex(),
160 argumentEntry.getName()
161 );
162 } else if (thing instanceof EntryReference) {
163 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
164 reference.entry = renameClassesInThing(renames, reference.entry);
165 reference.context = renameClassesInThing(renames, reference.context);
166 return thing;
167 }
168
169 return thing;
170 }
171}
diff --git a/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
new file mode 100644
index 0000000..2173eea
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -0,0 +1,81 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..72a9912
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/JarClassIterator.java
@@ -0,0 +1,137 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..3aac8bd
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -0,0 +1,734 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19import java.util.jar.JarFile;
20
21import javassist.CannotCompileException;
22import javassist.CtBehavior;
23import javassist.CtClass;
24import javassist.CtConstructor;
25import javassist.CtField;
26import javassist.bytecode.AccessFlag;
27import javassist.bytecode.Descriptor;
28import javassist.bytecode.FieldInfo;
29import javassist.expr.ConstructorCall;
30import javassist.expr.ExprEditor;
31import javassist.expr.FieldAccess;
32import javassist.expr.MethodCall;
33import javassist.expr.NewExpr;
34
35import com.google.common.collect.HashMultimap;
36import com.google.common.collect.Lists;
37import com.google.common.collect.Maps;
38import com.google.common.collect.Multimap;
39import com.google.common.collect.Sets;
40
41import cuchaz.enigma.Constants;
42import cuchaz.enigma.bytecode.ClassRenamer;
43import cuchaz.enigma.mapping.ArgumentEntry;
44import cuchaz.enigma.mapping.BehaviorEntry;
45import cuchaz.enigma.mapping.BehaviorEntryFactory;
46import cuchaz.enigma.mapping.ClassEntry;
47import cuchaz.enigma.mapping.ConstructorEntry;
48import cuchaz.enigma.mapping.Entry;
49import cuchaz.enigma.mapping.FieldEntry;
50import cuchaz.enigma.mapping.JavassistUtil;
51import cuchaz.enigma.mapping.MethodEntry;
52import cuchaz.enigma.mapping.Translator;
53
54public class JarIndex {
55
56 private Set<ClassEntry> m_obfClassEntries;
57 private TranslationIndex m_translationIndex;
58 private Multimap<String,String> m_interfaces;
59 private Map<Entry,Access> m_access;
60 private Map<FieldEntry,ClassEntry> m_fieldClasses; // TODO: this will become obsolete!
61 private Multimap<String,MethodEntry> m_methodImplementations;
62 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences;
63 private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences;
64 private Multimap<String,String> m_innerClasses;
65 private Map<String,String> m_outerClasses;
66 private Map<String,BehaviorEntry> m_anonymousClasses;
67
68 public JarIndex() {
69 m_obfClassEntries = Sets.newHashSet();
70 m_translationIndex = new TranslationIndex();
71 m_interfaces = HashMultimap.create();
72 m_access = Maps.newHashMap();
73 m_fieldClasses = Maps.newHashMap();
74 m_methodImplementations = HashMultimap.create();
75 m_behaviorReferences = HashMultimap.create();
76 m_fieldReferences = HashMultimap.create();
77 m_innerClasses = HashMultimap.create();
78 m_outerClasses = Maps.newHashMap();
79 m_anonymousClasses = Maps.newHashMap();
80 }
81
82 public void indexJar(JarFile jar, boolean buildInnerClasses) {
83
84 // step 1: read the class names
85 for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) {
86 if (classEntry.isInDefaultPackage()) {
87 // move out of default package
88 classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName());
89 }
90 m_obfClassEntries.add(classEntry);
91 }
92
93 // step 2: index field/method/constructor access
94 for (CtClass c : JarClassIterator.classes(jar)) {
95 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
96 for (CtField field : c.getDeclaredFields()) {
97 m_access.put(JavassistUtil.getFieldEntry(field), Access.get(field));
98 }
99 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
100 m_access.put(JavassistUtil.getBehaviorEntry(behavior), Access.get(behavior));
101 }
102 }
103
104 // step 3: index extends, implements, fields, and methods
105 for (CtClass c : JarClassIterator.classes(jar)) {
106 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
107 m_translationIndex.indexClass(c);
108 String className = Descriptor.toJvmName(c.getName());
109 for (String interfaceName : c.getClassFile().getInterfaces()) {
110 className = Descriptor.toJvmName(className);
111 interfaceName = Descriptor.toJvmName(interfaceName);
112 if (className.equals(interfaceName)) {
113 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
114 }
115 m_interfaces.put(className, interfaceName);
116 }
117 for (CtField field : c.getDeclaredFields()) {
118 indexField(field);
119 }
120 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
121 indexBehavior(behavior);
122 }
123 }
124
125 // step 4: index field, method, constructor references
126 for (CtClass c : JarClassIterator.classes(jar)) {
127 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
128 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
129 indexBehaviorReferences(behavior);
130 }
131 }
132
133 if (buildInnerClasses) {
134 // step 5: index inner classes and anonymous classes
135 for (CtClass c : JarClassIterator.classes(jar)) {
136 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
137 String outerClassName = findOuterClass(c);
138 if (outerClassName != null) {
139 String innerClassName = c.getSimpleName();
140 m_innerClasses.put(outerClassName, innerClassName);
141 boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null;
142 assert (innerWasAdded);
143
144 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName);
145 if (enclosingBehavior != null) {
146 m_anonymousClasses.put(innerClassName, enclosingBehavior);
147
148 // DEBUG
149 // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName );
150 } else {
151 // DEBUG
152 // System.out.println( "INNER: " + outerClassName + "$" + innerClassName );
153 }
154 }
155 }
156
157 // step 6: update other indices with inner class info
158 Map<String,String> renames = Maps.newHashMap();
159 for (Map.Entry<String,String> entry : m_outerClasses.entrySet()) {
160 renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey());
161 }
162 EntryRenamer.renameClassesInSet(renames, m_obfClassEntries);
163 m_translationIndex.renameClasses(renames);
164 EntryRenamer.renameClassesInMultimap(renames, m_interfaces);
165 EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations);
166 EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences);
167 EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences);
168 EntryRenamer.renameClassesInMap(renames, m_access);
169 }
170 }
171
172 private void indexField(CtField field) {
173 // get the field entry
174 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
175 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
176
177 // is the field a class type?
178 if (field.getSignature().startsWith("L")) {
179 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
180 m_fieldClasses.put(fieldEntry, fieldTypeEntry);
181 }
182 }
183
184 private void indexBehavior(CtBehavior behavior) {
185 // get the behavior entry
186 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
187 if (behaviorEntry instanceof MethodEntry) {
188 MethodEntry methodEntry = (MethodEntry)behaviorEntry;
189
190 // index implementation
191 m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
192 }
193 // looks like we don't care about constructors here
194 }
195
196 private void indexBehaviorReferences(CtBehavior behavior) {
197 // index method calls
198 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
199 try {
200 behavior.instrument(new ExprEditor() {
201 @Override
202 public void edit(MethodCall call) {
203 MethodEntry calledMethodEntry = JavassistUtil.getMethodEntry(call);
204 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry);
205 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
206 calledMethodEntry = new MethodEntry(
207 resolvedClassEntry,
208 calledMethodEntry.getName(),
209 calledMethodEntry.getSignature()
210 );
211 }
212 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
213 calledMethodEntry,
214 call.getMethodName(),
215 behaviorEntry
216 );
217 m_behaviorReferences.put(calledMethodEntry, reference);
218 }
219
220 @Override
221 public void edit(FieldAccess call) {
222 FieldEntry calledFieldEntry = JavassistUtil.getFieldEntry(call);
223 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry);
224 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
225 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
226 }
227 EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>(
228 calledFieldEntry,
229 call.getFieldName(),
230 behaviorEntry
231 );
232 m_fieldReferences.put(calledFieldEntry, reference);
233 }
234
235 @Override
236 public void edit(ConstructorCall call) {
237 ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call);
238 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
239 calledConstructorEntry,
240 call.getMethodName(),
241 behaviorEntry
242 );
243 m_behaviorReferences.put(calledConstructorEntry, reference);
244 }
245
246 @Override
247 public void edit(NewExpr call) {
248 ConstructorEntry calledConstructorEntry = JavassistUtil.getConstructorEntry(call);
249 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
250 calledConstructorEntry,
251 call.getClassName(),
252 behaviorEntry
253 );
254 m_behaviorReferences.put(calledConstructorEntry, reference);
255 }
256 });
257 } catch (CannotCompileException ex) {
258 throw new Error(ex);
259 }
260 }
261
262 private String findOuterClass(CtClass c) {
263
264 // inner classes:
265 // have constructors that can (illegally) set synthetic fields
266 // the outer class is the only class that calls constructors
267
268 // use the synthetic fields to find the synthetic constructors
269 for (CtConstructor constructor : c.getDeclaredConstructors()) {
270 Set<String> syntheticFieldTypes = Sets.newHashSet();
271 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
272 continue;
273 }
274
275 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
276 ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor);
277
278 // gather the classes from the illegally-set synthetic fields
279 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
280 for (String type : syntheticFieldTypes) {
281 if (type.startsWith("L")) {
282 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
283 if (isSaneOuterClass(outerClassEntry, classEntry)) {
284 illegallySetClasses.add(outerClassEntry);
285 }
286 }
287 }
288
289 // who calls this constructor?
290 Set<ClassEntry> callerClasses = Sets.newHashSet();
291 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
292
293 // make sure it's not a call to super
294 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
295
296 // is the entry a superclass of the context?
297 ClassEntry calledClassEntry = reference.entry.getClassEntry();
298 ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry());
299 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
300 // it's a super call, skip
301 continue;
302 }
303 }
304
305 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
306 callerClasses.add(reference.context.getClassEntry());
307 }
308 }
309
310 // do we have an answer yet?
311 if (callerClasses.isEmpty()) {
312 if (illegallySetClasses.size() == 1) {
313 return illegallySetClasses.iterator().next().getName();
314 } else {
315 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
316 }
317 } else {
318 if (callerClasses.size() == 1) {
319 return callerClasses.iterator().next().getName();
320 } else {
321 // multiple callers, do the illegally set classes narrow it down?
322 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
323 intersection.retainAll(illegallySetClasses);
324 if (intersection.size() == 1) {
325 return intersection.iterator().next().getName();
326 } else {
327 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
328 }
329 }
330 }
331 }
332
333 return null;
334 }
335
336 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) {
337
338 // clearly this would be silly
339 if (outerClassEntry.equals(innerClassEntry)) {
340 return false;
341 }
342
343 // is the outer class in the jar?
344 if (!m_obfClassEntries.contains(outerClassEntry)) {
345 return false;
346 }
347
348 return true;
349 }
350
351 @SuppressWarnings("unchecked")
352 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) {
353
354 // illegal constructors only set synthetic member fields, then call super()
355 String className = constructor.getDeclaringClass().getName();
356
357 // collect all the field accesses, constructor calls, and method calls
358 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
359 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
360 try {
361 constructor.instrument(new ExprEditor() {
362 @Override
363 public void edit(FieldAccess fieldAccess) {
364 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
365 illegalFieldWrites.add(fieldAccess);
366 }
367 }
368
369 @Override
370 public void edit(ConstructorCall constructorCall) {
371 constructorCalls.add(constructorCall);
372 }
373 });
374 } catch (CannotCompileException ex) {
375 // we're not compiling anything... this is stupid
376 throw new Error(ex);
377 }
378
379 // are there any illegal field writes?
380 if (illegalFieldWrites.isEmpty()) {
381 return false;
382 }
383
384 // are all the writes to synthetic fields?
385 for (FieldAccess fieldWrite : illegalFieldWrites) {
386
387 // all illegal writes have to be to the local class
388 if (!fieldWrite.getClassName().equals(className)) {
389 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
390 return false;
391 }
392
393 // find the field
394 FieldInfo fieldInfo = null;
395 for (FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields()) {
396 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
397 fieldInfo = info;
398 break;
399 }
400 }
401 if (fieldInfo == null) {
402 // field is in a superclass or something, can't be a local synthetic member
403 return false;
404 }
405
406 // is this field synthetic?
407 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
408 if (isSynthetic) {
409 syntheticFieldTypes.add(fieldInfo.getDescriptor());
410 } else {
411 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
412 return false;
413 }
414 }
415
416 // we passed all the tests!
417 return true;
418 }
419
420 private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) {
421
422 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
423
424 // anonymous classes:
425 // can't be abstract
426 // have only one constructor
427 // it's called exactly once by the outer class
428 // the type the instance is assigned to can't be this type
429
430 // is abstract?
431 if (Modifier.isAbstract(c.getModifiers())) {
432 return null;
433 }
434
435 // is there exactly one constructor?
436 if (c.getDeclaredConstructors().length != 1) {
437 return null;
438 }
439 CtConstructor constructor = c.getDeclaredConstructors()[0];
440
441 // is this constructor called exactly once?
442 ConstructorEntry constructorEntry = JavassistUtil.getConstructorEntry(constructor);
443 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
444 if (references.size() != 1) {
445 return null;
446 }
447
448 // does the caller use this type?
449 BehaviorEntry caller = references.iterator().next().context;
450 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
451 ClassEntry fieldClass = getFieldClass(fieldEntry);
452 if (fieldClass != null && fieldClass.equals(innerClassEntry)) {
453 // caller references this type, so it can't be anonymous
454 return null;
455 }
456 }
457 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
458 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) {
459 return null;
460 }
461 }
462
463 return caller;
464 }
465
466 public Set<ClassEntry> getObfClassEntries() {
467 return m_obfClassEntries;
468 }
469
470 public TranslationIndex getTranslationIndex() {
471 return m_translationIndex;
472 }
473
474 public Access getAccess(Entry entry) {
475 return m_access.get(entry);
476 }
477
478 public ClassEntry getFieldClass(FieldEntry fieldEntry) {
479 return m_fieldClasses.get(fieldEntry);
480 }
481
482 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
483
484 // get the root node
485 List<String> ancestry = Lists.newArrayList();
486 ancestry.add(obfClassEntry.getName());
487 for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) {
488 ancestry.add(classEntry.getName());
489 }
490 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
491 deobfuscatingTranslator,
492 ancestry.get(ancestry.size() - 1)
493 );
494
495 // expand all children recursively
496 rootNode.load(m_translationIndex, true);
497
498 return rootNode;
499 }
500
501 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
502
503 // is this even an interface?
504 if (isInterface(obfClassEntry.getClassName())) {
505 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
506 node.load(this);
507 return node;
508 }
509 return null;
510 }
511
512 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
513
514 // travel to the ancestor implementation
515 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
516 for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
517 MethodEntry ancestorMethodEntry = new MethodEntry(
518 new ClassEntry(ancestorClassEntry),
519 obfMethodEntry.getName(),
520 obfMethodEntry.getSignature()
521 );
522 if (containsObfBehavior(ancestorMethodEntry)) {
523 baseImplementationClassEntry = ancestorClassEntry;
524 }
525 }
526
527 // make a root node at the base
528 MethodEntry methodEntry = new MethodEntry(
529 baseImplementationClassEntry,
530 obfMethodEntry.getName(),
531 obfMethodEntry.getSignature()
532 );
533 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
534 deobfuscatingTranslator,
535 methodEntry,
536 containsObfBehavior(methodEntry)
537 );
538
539 // expand the full tree
540 rootNode.load(this, true);
541
542 return rootNode;
543 }
544
545 public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
546
547 MethodEntry interfaceMethodEntry;
548
549 // is this method on an interface?
550 if (isInterface(obfMethodEntry.getClassName())) {
551 interfaceMethodEntry = obfMethodEntry;
552 } else {
553 // get the interface class
554 List<MethodEntry> methodInterfaces = Lists.newArrayList();
555 for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) {
556 // is this method defined in this interface?
557 MethodEntry methodInterface = new MethodEntry(
558 new ClassEntry(interfaceName),
559 obfMethodEntry.getName(),
560 obfMethodEntry.getSignature()
561 );
562 if (containsObfBehavior(methodInterface)) {
563 methodInterfaces.add(methodInterface);
564 }
565 }
566 if (methodInterfaces.isEmpty()) {
567 return null;
568 }
569 if (methodInterfaces.size() > 1) {
570 throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!");
571 }
572 interfaceMethodEntry = methodInterfaces.get(0);
573 }
574
575 MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
576 rootNode.load(this);
577 return rootNode;
578 }
579
580 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
581 Set<MethodEntry> methodEntries = Sets.newHashSet();
582 getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry));
583 return methodEntries;
584 }
585
586 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
587 MethodEntry methodEntry = node.getMethodEntry();
588 if (containsObfBehavior(methodEntry)) {
589 // collect the entry
590 methodEntries.add(methodEntry);
591 }
592
593 // look at interface methods too
594 MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry);
595 if (implementations != null) {
596 getRelatedMethodImplementations(methodEntries, implementations);
597 }
598
599 // recurse
600 for (int i = 0; i < node.getChildCount(); i++) {
601 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode)node.getChildAt(i));
602 }
603 }
604
605 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
606 MethodEntry methodEntry = node.getMethodEntry();
607 if (containsObfBehavior(methodEntry)) {
608 // collect the entry
609 methodEntries.add(methodEntry);
610 }
611
612 // recurse
613 for (int i = 0; i < node.getChildCount(); i++) {
614 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i));
615 }
616 }
617
618 public Collection<EntryReference<FieldEntry,BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) {
619 return m_fieldReferences.get(fieldEntry);
620 }
621
622 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) {
623 // linear search is fast enough for now
624 Set<FieldEntry> fieldEntries = Sets.newHashSet();
625 for (EntryReference<FieldEntry,BehaviorEntry> reference : m_fieldReferences.values()) {
626 if (reference.context == behaviorEntry) {
627 fieldEntries.add(reference.entry);
628 }
629 }
630 return fieldEntries;
631 }
632
633 public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) {
634 return m_behaviorReferences.get(behaviorEntry);
635 }
636
637 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) {
638 // linear search is fast enough for now
639 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
640 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : m_behaviorReferences.values()) {
641 if (reference.context == behaviorEntry) {
642 behaviorEntries.add(reference.entry);
643 }
644 }
645 return behaviorEntries;
646 }
647
648 public Collection<String> getInnerClasses(String obfOuterClassName) {
649 return m_innerClasses.get(obfOuterClassName);
650 }
651
652 public String getOuterClass(String obfInnerClassName) {
653 // make sure we use the right name
654 if (new ClassEntry(obfInnerClassName).getPackageName() != null) {
655 throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName);
656 }
657 return m_outerClasses.get(obfInnerClassName);
658 }
659
660 public boolean isAnonymousClass(String obfInnerClassName) {
661 return m_anonymousClasses.containsKey(obfInnerClassName);
662 }
663
664 public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) {
665 return m_anonymousClasses.get(obfInnerClassName);
666 }
667
668 public Set<String> getInterfaces(String className) {
669 Set<String> interfaceNames = new HashSet<String>();
670 interfaceNames.addAll(m_interfaces.get(className));
671 for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) {
672 interfaceNames.addAll(m_interfaces.get(ancestor.getName()));
673 }
674 return interfaceNames;
675 }
676
677 public Set<String> getImplementingClasses(String targetInterfaceName) {
678 // linear search is fast enough for now
679 Set<String> classNames = Sets.newHashSet();
680 for (Map.Entry<String,String> entry : m_interfaces.entries()) {
681 String className = entry.getKey();
682 String interfaceName = entry.getValue();
683 if (interfaceName.equals(targetInterfaceName)) {
684 classNames.add(className);
685 m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className));
686 }
687 }
688 return classNames;
689 }
690
691 public boolean isInterface(String className) {
692 return m_interfaces.containsValue(className);
693 }
694
695 public boolean containsObfClass(ClassEntry obfClassEntry) {
696 return m_obfClassEntries.contains(obfClassEntry);
697 }
698
699 public boolean containsObfField(FieldEntry obfFieldEntry) {
700 return m_access.containsKey(obfFieldEntry);
701 }
702
703 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) {
704 return m_access.containsKey(obfBehaviorEntry);
705 }
706
707 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
708 // check the behavior
709 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
710 return false;
711 }
712
713 // check the argument
714 if (obfArgumentEntry.getIndex() >= obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size()) {
715 return false;
716 }
717
718 return true;
719 }
720
721 public boolean containsObfEntry(Entry obfEntry) {
722 if (obfEntry instanceof ClassEntry) {
723 return containsObfClass((ClassEntry)obfEntry);
724 } else if (obfEntry instanceof FieldEntry) {
725 return containsObfField((FieldEntry)obfEntry);
726 } else if (obfEntry instanceof BehaviorEntry) {
727 return containsObfBehavior((BehaviorEntry)obfEntry);
728 } else if (obfEntry instanceof ArgumentEntry) {
729 return containsObfArgument((ArgumentEntry)obfEntry);
730 } else {
731 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
732 }
733 }
734}
diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
new file mode 100644
index 0000000..1009226
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -0,0 +1,100 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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 // get all method implementations
67 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
68 for (String implementingClassName : index.getImplementingClasses(m_entry.getClassName())) {
69 MethodEntry methodEntry = new MethodEntry(
70 new ClassEntry(implementingClassName),
71 m_entry.getName(),
72 m_entry.getSignature()
73 );
74 if (index.containsObfBehavior(methodEntry)) {
75 nodes.add(new MethodImplementationsTreeNode(m_deobfuscatingTranslator, methodEntry));
76 }
77 }
78
79 // add them to this node
80 for (MethodImplementationsTreeNode node : nodes) {
81 this.add(node);
82 }
83 }
84
85 public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) {
86 // is this the node?
87 if (node.getMethodEntry().equals(entry)) {
88 return node;
89 }
90
91 // recurse
92 for (int i = 0; i < node.getChildCount(); i++) {
93 MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode)node.getChildAt(i), entry);
94 if (foundNode != null) {
95 return foundNode;
96 }
97 }
98 return null;
99 }
100}
diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
new file mode 100644
index 0000000..8718220
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -0,0 +1,114 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..2b08616
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -0,0 +1,18 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java
new file mode 100644
index 0000000..b43ab61
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndex.java
@@ -0,0 +1,173 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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
36 public SourceIndex(String source) {
37 m_source = source;
38 m_tokenToReference = Maps.newTreeMap();
39 m_referenceToTokens = HashMultimap.create();
40 m_declarationToToken = Maps.newHashMap();
41 m_lineOffsets = Lists.newArrayList();
42
43 // count the lines
44 m_lineOffsets.add(0);
45 for (int i = 0; i < source.length(); i++) {
46 if (source.charAt(i) == '\n') {
47 m_lineOffsets.add(i + 1);
48 }
49 }
50 }
51
52 public String getSource() {
53 return m_source;
54 }
55
56 public Token getToken(AstNode node) {
57
58 // get the text of the node
59 String name = "";
60 if (node instanceof Identifier) {
61 name = ((Identifier)node).getName();
62 }
63
64 // get a token for this node's region
65 Region region = node.getRegion();
66 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
67 // DEBUG
68 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
69 return null;
70 }
71 Token token = new Token(
72 toPos(region.getBeginLine(), region.getBeginColumn()),
73 toPos(region.getEndLine(), region.getEndColumn()),
74 m_source
75 );
76 if (token.start == 0) {
77 // DEBUG
78 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
79 return null;
80 }
81
82 // DEBUG
83 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
84
85 // for tokens representing inner classes, make sure we only get the simple name
86 int pos = name.lastIndexOf('$');
87 if (pos >= 0) {
88 token.end -= pos + 1;
89 }
90
91 return token;
92 }
93
94 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) {
95 Token token = getToken(node);
96 if (token != null) {
97 EntryReference<Entry,Entry> deobfReference = new EntryReference<Entry,Entry>(deobfEntry, token.text, deobfContext);
98 m_tokenToReference.put(token, deobfReference);
99 m_referenceToTokens.put(deobfReference, token);
100 }
101 }
102
103 public void addDeclaration(AstNode node, Entry deobfEntry) {
104 Token token = getToken(node);
105 if (token != null) {
106 EntryReference<Entry,Entry> reference = new EntryReference<Entry,Entry>(deobfEntry, token.text);
107 m_tokenToReference.put(token, reference);
108 m_referenceToTokens.put(reference, token);
109 m_declarationToToken.put(deobfEntry, token);
110 }
111 }
112
113 public Token getReferenceToken(int pos) {
114 Token token = m_tokenToReference.floorKey(new Token(pos, pos, null));
115 if (token != null && token.contains(pos)) {
116 return token;
117 }
118 return null;
119 }
120
121 public Collection<Token> getReferenceTokens(EntryReference<Entry,Entry> deobfReference) {
122 return m_referenceToTokens.get(deobfReference);
123 }
124
125 public EntryReference<Entry,Entry> getDeobfReference(Token token) {
126 if (token == null) {
127 return null;
128 }
129 return m_tokenToReference.get(token);
130 }
131
132 public void replaceDeobfReference(Token token, EntryReference<Entry,Entry> newDeobfReference) {
133 EntryReference<Entry,Entry> oldDeobfReference = m_tokenToReference.get(token);
134 m_tokenToReference.put(token, newDeobfReference);
135 Collection<Token> tokens = m_referenceToTokens.get(oldDeobfReference);
136 m_referenceToTokens.removeAll(oldDeobfReference);
137 m_referenceToTokens.putAll(newDeobfReference, tokens);
138 }
139
140 public Iterable<Token> referenceTokens() {
141 return m_tokenToReference.keySet();
142 }
143
144 public Iterable<Token> declarationTokens() {
145 return m_declarationToToken.values();
146 }
147
148 public Token getDeclarationToken(Entry deobfEntry) {
149 return m_declarationToToken.get(deobfEntry);
150 }
151
152 public int getLineNumber(int pos) {
153 // line number is 1-based
154 int line = 0;
155 for (Integer offset : m_lineOffsets) {
156 if (offset > pos) {
157 break;
158 }
159 line++;
160 }
161 return line;
162 }
163
164 public int getColumnNumber(int pos) {
165 // column number is 1-based
166 return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1;
167 }
168
169 private int toPos(int line, int col) {
170 // line and col are 1-based
171 return m_lineOffsets.get(line - 1) + col - 1;
172 }
173}
diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
new file mode 100644
index 0000000..4155128
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -0,0 +1,164 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.ConstructorDeclaration;
21import com.strobel.decompiler.languages.java.ast.IdentifierExpression;
22import com.strobel.decompiler.languages.java.ast.InvocationExpression;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.MethodDeclaration;
26import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
27import com.strobel.decompiler.languages.java.ast.ParameterDeclaration;
28import com.strobel.decompiler.languages.java.ast.SimpleType;
29import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression;
30import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression;
31
32import cuchaz.enigma.mapping.ArgumentEntry;
33import cuchaz.enigma.mapping.BehaviorEntry;
34import cuchaz.enigma.mapping.ClassEntry;
35import cuchaz.enigma.mapping.ConstructorEntry;
36import cuchaz.enigma.mapping.FieldEntry;
37import cuchaz.enigma.mapping.MethodEntry;
38import cuchaz.enigma.mapping.Signature;
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 visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
50 return recurse(node, index);
51 }
52
53 @Override
54 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
55 return recurse(node, index);
56 }
57
58 @Override
59 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
60 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
61
62 // get the behavior entry
63 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
64 BehaviorEntry behaviorEntry = null;
65 if (ref instanceof MethodReference) {
66 MethodReference methodRef = (MethodReference)ref;
67 if (methodRef.isConstructor()) {
68 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature()));
69 } else if (methodRef.isTypeInitializer()) {
70 behaviorEntry = new ConstructorEntry(classEntry);
71 } else {
72 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getSignature()));
73 }
74 }
75 if (behaviorEntry != null) {
76 // get the node for the token
77 AstNode tokenNode = null;
78 if (node.getTarget() instanceof MemberReferenceExpression) {
79 tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken();
80 } else if (node.getTarget() instanceof SuperReferenceExpression) {
81 tokenNode = node.getTarget();
82 } else if (node.getTarget() instanceof ThisReferenceExpression) {
83 tokenNode = node.getTarget();
84 }
85 if (tokenNode != null) {
86 index.addReference(tokenNode, behaviorEntry, m_behaviorEntry);
87 }
88 }
89
90 return recurse(node, index);
91 }
92
93 @Override
94 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
95 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
96 if (ref != null) {
97 // make sure this is actually a field
98 if (ref.getSignature().indexOf('(') >= 0) {
99 throw new Error("Expected a field here! got " + ref);
100 }
101
102 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
103 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName());
104 index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry);
105 }
106
107 return recurse(node, index);
108 }
109
110 @Override
111 public Void visitSimpleType(SimpleType node, SourceIndex index) {
112 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
113 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
114 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
115 index.addReference(node.getIdentifierToken(), classEntry, m_behaviorEntry);
116 }
117
118 return recurse(node, index);
119 }
120
121 @Override
122 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
123 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
124 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
125 MethodDefinition methodDef = (MethodDefinition)def.getMethod();
126 BehaviorEntry behaviorEntry;
127 if (methodDef.isConstructor()) {
128 behaviorEntry = new ConstructorEntry(classEntry, new Signature(methodDef.getSignature()));
129 } else {
130 behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), new Signature(methodDef.getSignature()));
131 }
132 ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName());
133 index.addDeclaration(node.getNameToken(), argumentEntry);
134
135 return recurse(node, index);
136 }
137
138 @Override
139 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
140 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
141 if (ref != null) {
142 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
143 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName());
144 index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry);
145 }
146
147 return recurse(node, index);
148 }
149
150 @Override
151 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
152 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
153 if (ref != null) {
154 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
155 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getSignature()));
156 if (node.getType() instanceof SimpleType) {
157 SimpleType simpleTypeNode = (SimpleType)node.getType();
158 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry);
159 }
160 }
161
162 return recurse(node, index);
163 }
164}
diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
new file mode 100644
index 0000000..7222035
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -0,0 +1,115 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.BehaviorEntryFactory;
30import cuchaz.enigma.mapping.ClassEntry;
31import cuchaz.enigma.mapping.ConstructorEntry;
32import cuchaz.enigma.mapping.FieldEntry;
33import cuchaz.enigma.mapping.Signature;
34
35public class SourceIndexClassVisitor extends SourceIndexVisitor {
36
37 private ClassEntry m_classEntry;
38
39 public SourceIndexClassVisitor(ClassEntry classEntry) {
40 m_classEntry = classEntry;
41 }
42
43 @Override
44 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
45 // is this this class, or a subtype?
46 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
47 ClassEntry classEntry = new ClassEntry(def.getInternalName());
48 if (!classEntry.equals(m_classEntry)) {
49 // it's a sub-type, recurse
50 index.addDeclaration(node.getNameToken(), classEntry);
51 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
52 }
53
54 return recurse(node, index);
55 }
56
57 @Override
58 public Void visitSimpleType(SimpleType node, SourceIndex index) {
59 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
60 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
61 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
62 index.addReference(node.getIdentifierToken(), classEntry, m_classEntry);
63 }
64
65 return recurse(node, index);
66 }
67
68 @Override
69 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
70 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
71 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
72 BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature());
73 AstNode tokenNode = node.getNameToken();
74 if (behaviorEntry instanceof ConstructorEntry) {
75 ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry;
76 if (constructorEntry.isStatic()) {
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 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
88 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(def.getSignature()));
89 index.addDeclaration(node.getNameToken(), constructorEntry);
90 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index);
91 }
92
93 @Override
94 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
95 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
96 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
97 FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName());
98 assert (node.getVariables().size() == 1);
99 VariableInitializer variable = node.getVariables().firstOrNullObject();
100 index.addDeclaration(variable.getNameToken(), fieldEntry);
101
102 return recurse(node, index);
103 }
104
105 @Override
106 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
107 // treat enum declarations as field declarations
108 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
109 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
110 FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName());
111 index.addDeclaration(node.getNameToken(), fieldEntry);
112
113 return recurse(node, index);
114 }
115}
diff --git a/src/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java
new file mode 100644
index 0000000..0d5bdc0
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -0,0 +1,452 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..481d2f4
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/Token.java
@@ -0,0 +1,56 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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..7597c3a
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/TranslationIndex.java
@@ -0,0 +1,227 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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.HashMap;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
23import java.util.zip.GZIPInputStream;
24import java.util.zip.GZIPOutputStream;
25
26import javassist.CtBehavior;
27import javassist.CtClass;
28import javassist.CtField;
29
30import com.google.common.collect.HashMultimap;
31import com.google.common.collect.Lists;
32import com.google.common.collect.Maps;
33import com.google.common.collect.Multimap;
34
35import cuchaz.enigma.mapping.ArgumentEntry;
36import cuchaz.enigma.mapping.BehaviorEntry;
37import cuchaz.enigma.mapping.ClassEntry;
38import cuchaz.enigma.mapping.Entry;
39import cuchaz.enigma.mapping.FieldEntry;
40import cuchaz.enigma.mapping.JavassistUtil;
41import cuchaz.enigma.mapping.Translator;
42
43public class TranslationIndex implements Serializable {
44
45 private static final long serialVersionUID = 738687982126844179L;
46
47 private Map<ClassEntry,ClassEntry> m_superclasses;
48 private Multimap<ClassEntry,FieldEntry> m_fieldEntries;
49 private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries;
50
51 public TranslationIndex() {
52 m_superclasses = Maps.newHashMap();
53 m_fieldEntries = HashMultimap.create();
54 m_behaviorEntries = HashMultimap.create();
55 }
56
57 public TranslationIndex(TranslationIndex other, Translator translator) {
58
59 // translate the superclasses
60 m_superclasses = Maps.newHashMap();
61 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_superclasses.entrySet()) {
62 m_superclasses.put(
63 translator.translateEntry(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue())
65 );
66 }
67
68 // translate the fields
69 m_fieldEntries = HashMultimap.create();
70 for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) {
71 m_fieldEntries.put(
72 translator.translateEntry(mapEntry.getKey()),
73 translator.translateEntry(mapEntry.getValue())
74 );
75 }
76
77 m_behaviorEntries = HashMultimap.create();
78 for (Map.Entry<ClassEntry,BehaviorEntry> mapEntry : other.m_behaviorEntries.entries()) {
79 m_behaviorEntries.put(
80 translator.translateEntry(mapEntry.getKey()),
81 translator.translateEntry(mapEntry.getValue())
82 );
83 }
84 }
85
86 public void indexClass(CtClass c) {
87
88 ClassEntry classEntry = JavassistUtil.getClassEntry(c);
89
90 // add the superclass
91 ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c);
92 if (!isJre(classEntry) && superclassEntry != null && !isJre(superclassEntry)) {
93 m_superclasses.put(classEntry, superclassEntry);
94 }
95
96 // add fields
97 for (CtField field : c.getDeclaredFields()) {
98 FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field);
99 m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
100 }
101
102 // add behaviors
103 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
104 BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior);
105 m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
106 }
107 }
108
109 public void renameClasses(Map<String,String> renames) {
110 EntryRenamer.renameClassesInMap(renames, m_superclasses);
111 EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries);
112 EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries);
113 }
114
115 public ClassEntry getSuperclass(ClassEntry classEntry) {
116 return m_superclasses.get(classEntry);
117 }
118
119 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
120 List<ClassEntry> ancestors = Lists.newArrayList();
121 while (classEntry != null) {
122 classEntry = getSuperclass(classEntry);
123 if (classEntry != null) {
124 ancestors.add(classEntry);
125 }
126 }
127 return ancestors;
128 }
129
130 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
131 // linear search is fast enough for now
132 List<ClassEntry> subclasses = Lists.newArrayList();
133 for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) {
134 ClassEntry subclass = entry.getKey();
135 ClassEntry superclass = entry.getValue();
136 if (classEntry.equals(superclass)) {
137 subclasses.add(subclass);
138 }
139 }
140 return subclasses;
141 }
142
143 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
144 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
145 out.add(subclassEntry);
146 getSubclassesRecursively(out, subclassEntry);
147 }
148 }
149
150 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
151 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
152 out.add(subclassEntry.getName());
153 getSubclassNamesRecursively(out, subclassEntry);
154 }
155 }
156
157 public boolean entryExists(Entry entry) {
158 if (entry instanceof FieldEntry) {
159 return fieldExists((FieldEntry)entry);
160 } else if (entry instanceof BehaviorEntry) {
161 return behaviorExists((BehaviorEntry)entry);
162 } else if (entry instanceof ArgumentEntry) {
163 return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry());
164 }
165 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
166 }
167
168 public boolean fieldExists(FieldEntry fieldEntry) {
169 return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
170 }
171
172 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
173 return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
174 }
175
176 public ClassEntry resolveEntryClass(Entry entry) {
177
178 if (entry instanceof ClassEntry) {
179 return (ClassEntry)entry;
180 }
181
182 // this entry could refer to a method on a class where the method is not actually implemented
183 // travel up the inheritance tree to find the closest implementation
184 while (!entryExists(entry)) {
185
186 // is there a parent class?
187 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
188 if (superclassEntry == null) {
189 // this is probably a method from a class in a library
190 // we can't trace the implementation up any higher unless we index the library
191 return null;
192 }
193
194 // move up to the parent class
195 entry = entry.cloneToNewClass(superclassEntry);
196 }
197 return entry.getClassEntry();
198 }
199
200 private boolean isJre(ClassEntry classEntry) {
201 String packageName = classEntry.getPackageName();
202 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
203 }
204
205 public void write(OutputStream out)
206 throws IOException {
207 GZIPOutputStream gzipout = new GZIPOutputStream(out);
208 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
209 oout.writeObject(m_superclasses);
210 oout.writeObject(m_fieldEntries);
211 oout.writeObject(m_behaviorEntries);
212 gzipout.finish();
213 }
214
215 @SuppressWarnings("unchecked")
216 public void read(InputStream in)
217 throws IOException {
218 try {
219 ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in));
220 m_superclasses = (HashMap<ClassEntry,ClassEntry>)oin.readObject();
221 m_fieldEntries = (HashMultimap<ClassEntry,FieldEntry>)oin.readObject();
222 m_behaviorEntries = (HashMultimap<ClassEntry,BehaviorEntry>)oin.readObject();
223 } catch (ClassNotFoundException ex) {
224 throw new Error(ex);
225 }
226 }
227}
diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java
new file mode 100644
index 0000000..23f8089
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java
@@ -0,0 +1,512 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.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}