summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar jeff2015-02-03 22:00:53 -0500
committerGravatar jeff2015-02-03 22:00:53 -0500
commit52ab426d8fad3dbee7e728f523a35af94facebda (patch)
tree146fadfd8e639a909d6c1d6a193e7eddeab0be4a /src/cuchaz/enigma/analysis
downloadenigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.tar.gz
enigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.tar.xz
enigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.zip
oops, don't depend on local procyon project
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.java828
-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.java163
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java114
-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, 3573 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..c96d3bc
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -0,0 +1,828 @@
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.CtMethod;
27import javassist.NotFoundException;
28import javassist.bytecode.AccessFlag;
29import javassist.bytecode.Descriptor;
30import javassist.bytecode.FieldInfo;
31import javassist.expr.ConstructorCall;
32import javassist.expr.ExprEditor;
33import javassist.expr.FieldAccess;
34import javassist.expr.MethodCall;
35import javassist.expr.NewExpr;
36
37import com.google.common.collect.HashMultimap;
38import com.google.common.collect.Lists;
39import com.google.common.collect.Maps;
40import com.google.common.collect.Multimap;
41import com.google.common.collect.Sets;
42
43import cuchaz.enigma.Constants;
44import cuchaz.enigma.bytecode.ClassRenamer;
45import cuchaz.enigma.mapping.ArgumentEntry;
46import cuchaz.enigma.mapping.BehaviorEntry;
47import cuchaz.enigma.mapping.BehaviorEntryFactory;
48import cuchaz.enigma.mapping.ClassEntry;
49import cuchaz.enigma.mapping.ConstructorEntry;
50import cuchaz.enigma.mapping.Entry;
51import cuchaz.enigma.mapping.FieldEntry;
52import cuchaz.enigma.mapping.MethodEntry;
53import cuchaz.enigma.mapping.SignatureUpdater;
54import cuchaz.enigma.mapping.Translator;
55
56public class JarIndex {
57
58 private Set<ClassEntry> m_obfClassEntries;
59 private TranslationIndex m_translationIndex;
60 private Multimap<String,String> m_interfaces;
61 private Map<Entry,Access> m_access;
62 private Map<FieldEntry,ClassEntry> m_fieldClasses;
63 private Multimap<String,MethodEntry> m_methodImplementations;
64 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences;
65 private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences;
66 private Multimap<String,String> m_innerClasses;
67 private Map<String,String> m_outerClasses;
68 private Map<String,BehaviorEntry> m_anonymousClasses;
69 private Map<MethodEntry,MethodEntry> m_bridgeMethods;
70
71 public JarIndex() {
72 m_obfClassEntries = Sets.newHashSet();
73 m_translationIndex = new TranslationIndex();
74 m_interfaces = HashMultimap.create();
75 m_access = Maps.newHashMap();
76 m_fieldClasses = Maps.newHashMap();
77 m_methodImplementations = HashMultimap.create();
78 m_behaviorReferences = HashMultimap.create();
79 m_fieldReferences = HashMultimap.create();
80 m_innerClasses = HashMultimap.create();
81 m_outerClasses = Maps.newHashMap();
82 m_anonymousClasses = Maps.newHashMap();
83 m_bridgeMethods = Maps.newHashMap();
84 }
85
86 public void indexJar(JarFile jar, boolean buildInnerClasses) {
87 // step 1: read the class names
88 for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) {
89 if (classEntry.isInDefaultPackage()) {
90 // move out of default package
91 classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName());
92 }
93 m_obfClassEntries.add(classEntry);
94 }
95
96 // step 2: index field/method/constructor access
97 for (CtClass c : JarClassIterator.classes(jar)) {
98 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
99 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
100 for (CtField field : c.getDeclaredFields()) {
101 FieldEntry fieldEntry = new FieldEntry(classEntry, field.getName());
102 m_access.put(fieldEntry, Access.get(field));
103 }
104 for (CtMethod method : c.getDeclaredMethods()) {
105 MethodEntry methodEntry = new MethodEntry(classEntry, method.getName(), method.getSignature());
106 m_access.put(methodEntry, Access.get(method));
107 }
108 for (CtConstructor constructor : c.getDeclaredConstructors()) {
109 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, constructor.getSignature());
110 m_access.put(constructorEntry, Access.get(constructor));
111 }
112 }
113
114 // step 3: index extends, implements, fields, and methods
115 for (CtClass c : JarClassIterator.classes(jar)) {
116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
117 m_translationIndex.indexClass(c);
118 String className = Descriptor.toJvmName(c.getName());
119 for (String interfaceName : c.getClassFile().getInterfaces()) {
120 className = Descriptor.toJvmName(className);
121 interfaceName = Descriptor.toJvmName(interfaceName);
122 if (className.equals(interfaceName)) {
123 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
124 }
125 m_interfaces.put(className, interfaceName);
126 }
127 for (CtField field : c.getDeclaredFields()) {
128 indexField(field);
129 }
130 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
131 indexBehavior(behavior);
132 }
133 }
134
135 // step 4: index field, method, constructor references
136 for (CtClass c : JarClassIterator.classes(jar)) {
137 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
138 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
139 indexBehaviorReferences(behavior);
140 }
141 }
142
143 if (buildInnerClasses) {
144 // step 5: index inner classes and anonymous classes
145 for (CtClass c : JarClassIterator.classes(jar)) {
146 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
147 String outerClassName = findOuterClass(c);
148 if (outerClassName != null) {
149 String innerClassName = c.getSimpleName();
150 m_innerClasses.put(outerClassName, innerClassName);
151 boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null;
152 assert (innerWasAdded);
153
154 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName);
155 if (enclosingBehavior != null) {
156 m_anonymousClasses.put(innerClassName, enclosingBehavior);
157
158 // DEBUG
159 // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName );
160 } else {
161 // DEBUG
162 // System.out.println( "INNER: " + outerClassName + "$" + innerClassName );
163 }
164 }
165 }
166
167 // step 6: update other indices with inner class info
168 Map<String,String> renames = Maps.newHashMap();
169 for (Map.Entry<String,String> entry : m_outerClasses.entrySet()) {
170 renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey());
171 }
172 EntryRenamer.renameClassesInSet(renames, m_obfClassEntries);
173 m_translationIndex.renameClasses(renames);
174 EntryRenamer.renameClassesInMultimap(renames, m_interfaces);
175 EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations);
176 EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences);
177 EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences);
178 EntryRenamer.renameClassesInMap(renames, m_bridgeMethods);
179 EntryRenamer.renameClassesInMap(renames, m_access);
180 }
181
182 // step 6: update other indices with bridge method info
183 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_methodImplementations);
184 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_behaviorReferences);
185 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_fieldReferences);
186 EntryRenamer.renameMethodsInMap(m_bridgeMethods, m_access);
187 }
188
189 private void indexField(CtField field) {
190 // get the field entry
191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
193
194 // is the field a class type?
195 if (field.getSignature().startsWith("L")) {
196 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
197 m_fieldClasses.put(fieldEntry, fieldTypeEntry);
198 }
199 }
200
201 private void indexBehavior(CtBehavior behavior) {
202 // get the behavior entry
203 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
204 if (behaviorEntry instanceof MethodEntry) {
205 MethodEntry methodEntry = (MethodEntry)behaviorEntry;
206
207 // index implementation
208 m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
209
210 // look for bridge methods
211 CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior);
212 if (bridgedMethod != null) {
213 MethodEntry bridgedMethodEntry = new MethodEntry(
214 behaviorEntry.getClassEntry(),
215 bridgedMethod.getName(),
216 bridgedMethod.getSignature()
217 );
218 m_bridgeMethods.put(bridgedMethodEntry, methodEntry);
219 }
220 }
221 // looks like we don't care about constructors here
222 }
223
224 private void indexBehaviorReferences(CtBehavior behavior) {
225 // index method calls
226 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
227 try {
228 behavior.instrument(new ExprEditor() {
229 @Override
230 public void edit(MethodCall call) {
231 MethodEntry calledMethodEntry = new MethodEntry(
232 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
233 call.getMethodName(),
234 call.getSignature()
235 );
236 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry);
237 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
238 calledMethodEntry = new MethodEntry(
239 resolvedClassEntry,
240 call.getMethodName(),
241 call.getSignature()
242 );
243 }
244 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
245 calledMethodEntry,
246 call.getMethodName(),
247 behaviorEntry
248 );
249 m_behaviorReferences.put(calledMethodEntry, reference);
250 }
251
252 @Override
253 public void edit(FieldAccess call) {
254 FieldEntry calledFieldEntry = new FieldEntry(
255 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
256 call.getFieldName()
257 );
258 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry);
259 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
260 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
261 }
262 EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>(
263 calledFieldEntry,
264 call.getFieldName(),
265 behaviorEntry
266 );
267 m_fieldReferences.put(calledFieldEntry, reference);
268 }
269
270 @Override
271 public void edit(ConstructorCall call) {
272 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
273 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
274 call.getSignature()
275 );
276 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
277 calledConstructorEntry,
278 call.getMethodName(),
279 behaviorEntry
280 );
281 m_behaviorReferences.put(calledConstructorEntry, reference);
282 }
283
284 @Override
285 public void edit(NewExpr call) {
286 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
287 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
288 call.getSignature()
289 );
290 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
291 calledConstructorEntry,
292 call.getClassName(),
293 behaviorEntry
294 );
295 m_behaviorReferences.put(calledConstructorEntry, reference);
296 }
297 });
298 } catch (CannotCompileException ex) {
299 throw new Error(ex);
300 }
301 }
302
303 private CtMethod getBridgedMethod(CtMethod method) {
304
305 // bridge methods just call another method, cast it to the return type, and return the result
306 // let's see if we can detect this scenario
307
308 // skip non-synthetic methods
309 if ( (method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
310 return null;
311 }
312
313 // get all the called methods
314 final List<MethodCall> methodCalls = Lists.newArrayList();
315 try {
316 method.instrument(new ExprEditor() {
317 @Override
318 public void edit(MethodCall call) {
319 methodCalls.add(call);
320 }
321 });
322 } catch (CannotCompileException ex) {
323 // this is stupid... we're not even compiling anything
324 throw new Error(ex);
325 }
326
327 // is there just one?
328 if (methodCalls.size() != 1) {
329 return null;
330 }
331 MethodCall call = methodCalls.get(0);
332
333 try {
334 // we have a bridge method!
335 return call.getMethod();
336 } catch (NotFoundException ex) {
337 // can't find the type? not a bridge method
338 return null;
339 }
340 }
341
342 private String findOuterClass(CtClass c) {
343
344 // inner classes:
345 // have constructors that can (illegally) set synthetic fields
346 // the outer class is the only class that calls constructors
347
348 // use the synthetic fields to find the synthetic constructors
349 for (CtConstructor constructor : c.getDeclaredConstructors()) {
350 Set<String> syntheticFieldTypes = Sets.newHashSet();
351 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
352 continue;
353 }
354
355 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
356 ConstructorEntry constructorEntry = new ConstructorEntry(
357 classEntry,
358 constructor.getMethodInfo().getDescriptor()
359 );
360
361 // gather the classes from the illegally-set synthetic fields
362 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
363 for (String type : syntheticFieldTypes) {
364 if (type.startsWith("L")) {
365 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
366 if (isSaneOuterClass(outerClassEntry, classEntry)) {
367 illegallySetClasses.add(outerClassEntry);
368 }
369 }
370 }
371
372 // who calls this constructor?
373 Set<ClassEntry> callerClasses = Sets.newHashSet();
374 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
375
376 // make sure it's not a call to super
377 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
378
379 // is the entry a superclass of the context?
380 ClassEntry calledClassEntry = reference.entry.getClassEntry();
381 ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry());
382 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
383 // it's a super call, skip
384 continue;
385 }
386 }
387
388 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
389 callerClasses.add(reference.context.getClassEntry());
390 }
391 }
392
393 // do we have an answer yet?
394 if (callerClasses.isEmpty()) {
395 if (illegallySetClasses.size() == 1) {
396 return illegallySetClasses.iterator().next().getName();
397 } else {
398 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
399 }
400 } else {
401 if (callerClasses.size() == 1) {
402 return callerClasses.iterator().next().getName();
403 } else {
404 // multiple callers, do the illegally set classes narrow it down?
405 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
406 intersection.retainAll(illegallySetClasses);
407 if (intersection.size() == 1) {
408 return intersection.iterator().next().getName();
409 } else {
410 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
411 }
412 }
413 }
414 }
415
416 return null;
417 }
418
419 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) {
420
421 // clearly this would be silly
422 if (outerClassEntry.equals(innerClassEntry)) {
423 return false;
424 }
425
426 // is the outer class in the jar?
427 if (!m_obfClassEntries.contains(outerClassEntry)) {
428 return false;
429 }
430
431 return true;
432 }
433
434 @SuppressWarnings("unchecked")
435 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) {
436
437 // illegal constructors only set synthetic member fields, then call super()
438 String className = constructor.getDeclaringClass().getName();
439
440 // collect all the field accesses, constructor calls, and method calls
441 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
442 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
443 try {
444 constructor.instrument(new ExprEditor() {
445 @Override
446 public void edit(FieldAccess fieldAccess) {
447 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
448 illegalFieldWrites.add(fieldAccess);
449 }
450 }
451
452 @Override
453 public void edit(ConstructorCall constructorCall) {
454 constructorCalls.add(constructorCall);
455 }
456 });
457 } catch (CannotCompileException ex) {
458 // we're not compiling anything... this is stupid
459 throw new Error(ex);
460 }
461
462 // are there any illegal field writes?
463 if (illegalFieldWrites.isEmpty()) {
464 return false;
465 }
466
467 // are all the writes to synthetic fields?
468 for (FieldAccess fieldWrite : illegalFieldWrites) {
469
470 // all illegal writes have to be to the local class
471 if (!fieldWrite.getClassName().equals(className)) {
472 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
473 return false;
474 }
475
476 // find the field
477 FieldInfo fieldInfo = null;
478 for (FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields()) {
479 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
480 fieldInfo = info;
481 break;
482 }
483 }
484 if (fieldInfo == null) {
485 // field is in a superclass or something, can't be a local synthetic member
486 return false;
487 }
488
489 // is this field synthetic?
490 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
491 if (isSynthetic) {
492 syntheticFieldTypes.add(fieldInfo.getDescriptor());
493 } else {
494 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
495 return false;
496 }
497 }
498
499 // we passed all the tests!
500 return true;
501 }
502
503 private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) {
504
505 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
506
507 // anonymous classes:
508 // can't be abstract
509 // have only one constructor
510 // it's called exactly once by the outer class
511 // the type the instance is assigned to can't be this type
512
513 // is abstract?
514 if (Modifier.isAbstract(c.getModifiers())) {
515 return null;
516 }
517
518 // is there exactly one constructor?
519 if (c.getDeclaredConstructors().length != 1) {
520 return null;
521 }
522 CtConstructor constructor = c.getDeclaredConstructors()[0];
523
524 // is this constructor called exactly once?
525 ConstructorEntry constructorEntry = new ConstructorEntry(
526 innerClassEntry,
527 constructor.getMethodInfo().getDescriptor()
528 );
529 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
530 if (references.size() != 1) {
531 return null;
532 }
533
534 // does the caller use this type?
535 BehaviorEntry caller = references.iterator().next().context;
536 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
537 ClassEntry fieldClass = getFieldClass(fieldEntry);
538 if (fieldClass != null && fieldClass.equals(innerClassEntry)) {
539 // caller references this type, so it can't be anonymous
540 return null;
541 }
542 }
543 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
544 // get the class types from the signature
545 for (String className : SignatureUpdater.getClasses(behaviorEntry.getSignature())) {
546 if (className.equals(innerClassEntry.getName())) {
547 // caller references this type, so it can't be anonymous
548 return null;
549 }
550 }
551 }
552
553 return caller;
554 }
555
556 public Set<ClassEntry> getObfClassEntries() {
557 return m_obfClassEntries;
558 }
559
560 public TranslationIndex getTranslationIndex() {
561 return m_translationIndex;
562 }
563
564 public Access getAccess(Entry entry) {
565 return m_access.get(entry);
566 }
567
568 public ClassEntry getFieldClass(FieldEntry fieldEntry) {
569 return m_fieldClasses.get(fieldEntry);
570 }
571
572 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
573
574 // get the root node
575 List<String> ancestry = Lists.newArrayList();
576 ancestry.add(obfClassEntry.getName());
577 for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) {
578 ancestry.add(classEntry.getName());
579 }
580 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
581 deobfuscatingTranslator,
582 ancestry.get(ancestry.size() - 1)
583 );
584
585 // expand all children recursively
586 rootNode.load(m_translationIndex, true);
587
588 return rootNode;
589 }
590
591 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
592
593 // is this even an interface?
594 if (isInterface(obfClassEntry.getClassName())) {
595 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
596 node.load(this);
597 return node;
598 }
599 return null;
600 }
601
602 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
603
604 // travel to the ancestor implementation
605 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
606 for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
607 MethodEntry ancestorMethodEntry = new MethodEntry(
608 new ClassEntry(ancestorClassEntry),
609 obfMethodEntry.getName(),
610 obfMethodEntry.getSignature()
611 );
612 if (containsObfBehavior(ancestorMethodEntry)) {
613 baseImplementationClassEntry = ancestorClassEntry;
614 }
615 }
616
617 // make a root node at the base
618 MethodEntry methodEntry = new MethodEntry(
619 baseImplementationClassEntry,
620 obfMethodEntry.getName(),
621 obfMethodEntry.getSignature()
622 );
623 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
624 deobfuscatingTranslator,
625 methodEntry,
626 containsObfBehavior(methodEntry)
627 );
628
629 // expand the full tree
630 rootNode.load(this, true);
631
632 return rootNode;
633 }
634
635 public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
636
637 MethodEntry interfaceMethodEntry;
638
639 // is this method on an interface?
640 if (isInterface(obfMethodEntry.getClassName())) {
641 interfaceMethodEntry = obfMethodEntry;
642 } else {
643 // get the interface class
644 List<MethodEntry> methodInterfaces = Lists.newArrayList();
645 for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) {
646 // is this method defined in this interface?
647 MethodEntry methodInterface = new MethodEntry(
648 new ClassEntry(interfaceName),
649 obfMethodEntry.getName(),
650 obfMethodEntry.getSignature()
651 );
652 if (containsObfBehavior(methodInterface)) {
653 methodInterfaces.add(methodInterface);
654 }
655 }
656 if (methodInterfaces.isEmpty()) {
657 return null;
658 }
659 if (methodInterfaces.size() > 1) {
660 throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!");
661 }
662 interfaceMethodEntry = methodInterfaces.get(0);
663 }
664
665 MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
666 rootNode.load(this);
667 return rootNode;
668 }
669
670 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
671 Set<MethodEntry> methodEntries = Sets.newHashSet();
672 getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry));
673 return methodEntries;
674 }
675
676 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
677 MethodEntry methodEntry = node.getMethodEntry();
678 if (containsObfBehavior(methodEntry)) {
679 // collect the entry
680 methodEntries.add(methodEntry);
681 }
682
683 // look at interface methods too
684 MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry);
685 if (implementations != null) {
686 getRelatedMethodImplementations(methodEntries, implementations);
687 }
688
689 // recurse
690 for (int i = 0; i < node.getChildCount(); i++) {
691 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode)node.getChildAt(i));
692 }
693 }
694
695 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
696 MethodEntry methodEntry = node.getMethodEntry();
697 if (containsObfBehavior(methodEntry)) {
698 // collect the entry
699 methodEntries.add(methodEntry);
700 }
701
702 // recurse
703 for (int i = 0; i < node.getChildCount(); i++) {
704 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i));
705 }
706 }
707
708 public Collection<EntryReference<FieldEntry,BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) {
709 return m_fieldReferences.get(fieldEntry);
710 }
711
712 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) {
713 // linear search is fast enough for now
714 Set<FieldEntry> fieldEntries = Sets.newHashSet();
715 for (EntryReference<FieldEntry,BehaviorEntry> reference : m_fieldReferences.values()) {
716 if (reference.context == behaviorEntry) {
717 fieldEntries.add(reference.entry);
718 }
719 }
720 return fieldEntries;
721 }
722
723 public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) {
724 return m_behaviorReferences.get(behaviorEntry);
725 }
726
727 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) {
728 // linear search is fast enough for now
729 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
730 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : m_behaviorReferences.values()) {
731 if (reference.context == behaviorEntry) {
732 behaviorEntries.add(reference.entry);
733 }
734 }
735 return behaviorEntries;
736 }
737
738 public Collection<String> getInnerClasses(String obfOuterClassName) {
739 return m_innerClasses.get(obfOuterClassName);
740 }
741
742 public String getOuterClass(String obfInnerClassName) {
743 // make sure we use the right name
744 if (new ClassEntry(obfInnerClassName).getPackageName() != null) {
745 throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName);
746 }
747 return m_outerClasses.get(obfInnerClassName);
748 }
749
750 public boolean isAnonymousClass(String obfInnerClassName) {
751 return m_anonymousClasses.containsKey(obfInnerClassName);
752 }
753
754 public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) {
755 return m_anonymousClasses.get(obfInnerClassName);
756 }
757
758 public Set<String> getInterfaces(String className) {
759 Set<String> interfaceNames = new HashSet<String>();
760 interfaceNames.addAll(m_interfaces.get(className));
761 for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) {
762 interfaceNames.addAll(m_interfaces.get(ancestor.getName()));
763 }
764 return interfaceNames;
765 }
766
767 public Set<String> getImplementingClasses(String targetInterfaceName) {
768 // linear search is fast enough for now
769 Set<String> classNames = Sets.newHashSet();
770 for (Map.Entry<String,String> entry : m_interfaces.entries()) {
771 String className = entry.getKey();
772 String interfaceName = entry.getValue();
773 if (interfaceName.equals(targetInterfaceName)) {
774 classNames.add(className);
775 m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className));
776 }
777 }
778 return classNames;
779 }
780
781 public boolean isInterface(String className) {
782 return m_interfaces.containsValue(className);
783 }
784
785 public MethodEntry getBridgeMethod(MethodEntry methodEntry) {
786 return m_bridgeMethods.get(methodEntry);
787 }
788
789 public boolean containsObfClass(ClassEntry obfClassEntry) {
790 return m_obfClassEntries.contains(obfClassEntry);
791 }
792
793 public boolean containsObfField(FieldEntry obfFieldEntry) {
794 return m_access.containsKey(obfFieldEntry);
795 }
796
797 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) {
798 return m_access.containsKey(obfBehaviorEntry);
799 }
800
801 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
802 // check the behavior
803 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
804 return false;
805 }
806
807 // check the argument
808 if (obfArgumentEntry.getIndex() >= Descriptor.numOfParameters(obfArgumentEntry.getBehaviorEntry().getSignature())) {
809 return false;
810 }
811
812 return true;
813 }
814
815 public boolean containsObfEntry(Entry obfEntry) {
816 if (obfEntry instanceof ClassEntry) {
817 return containsObfClass((ClassEntry)obfEntry);
818 } else if (obfEntry instanceof FieldEntry) {
819 return containsObfField((FieldEntry)obfEntry);
820 } else if (obfEntry instanceof BehaviorEntry) {
821 return containsObfBehavior((BehaviorEntry)obfEntry);
822 } else if (obfEntry instanceof ArgumentEntry) {
823 return containsObfArgument((ArgumentEntry)obfEntry);
824 } else {
825 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
826 }
827 }
828}
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..43c1749
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -0,0 +1,163 @@
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;
38
39public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
40
41 private BehaviorEntry m_behaviorEntry;
42
43 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) {
44 m_behaviorEntry = behaviorEntry;
45 }
46
47 @Override
48 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
49 return recurse(node, index);
50 }
51
52 @Override
53 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
54 return recurse(node, index);
55 }
56
57 @Override
58 public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) {
59 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
60
61 // get the behavior entry
62 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
63 BehaviorEntry behaviorEntry = null;
64 if (ref instanceof MethodReference) {
65 MethodReference methodRef = (MethodReference)ref;
66 if (methodRef.isConstructor()) {
67 behaviorEntry = new ConstructorEntry(classEntry, ref.getSignature());
68 } else if (methodRef.isTypeInitializer()) {
69 behaviorEntry = new ConstructorEntry(classEntry);
70 } else {
71 behaviorEntry = new MethodEntry(classEntry, ref.getName(), ref.getSignature());
72 }
73 }
74 if (behaviorEntry != null) {
75 // get the node for the token
76 AstNode tokenNode = null;
77 if (node.getTarget() instanceof MemberReferenceExpression) {
78 tokenNode = ((MemberReferenceExpression)node.getTarget()).getMemberNameToken();
79 } else if (node.getTarget() instanceof SuperReferenceExpression) {
80 tokenNode = node.getTarget();
81 } else if (node.getTarget() instanceof ThisReferenceExpression) {
82 tokenNode = node.getTarget();
83 }
84 if (tokenNode != null) {
85 index.addReference(tokenNode, behaviorEntry, m_behaviorEntry);
86 }
87 }
88
89 return recurse(node, index);
90 }
91
92 @Override
93 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
94 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
95 if (ref != null) {
96 // make sure this is actually a field
97 if (ref.getSignature().indexOf('(') >= 0) {
98 throw new Error("Expected a field here! got " + ref);
99 }
100
101 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
102 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName());
103 index.addReference(node.getMemberNameToken(), fieldEntry, m_behaviorEntry);
104 }
105
106 return recurse(node, index);
107 }
108
109 @Override
110 public Void visitSimpleType(SimpleType node, SourceIndex index) {
111 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
112 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
113 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
114 index.addReference(node.getIdentifierToken(), classEntry, m_behaviorEntry);
115 }
116
117 return recurse(node, index);
118 }
119
120 @Override
121 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
122 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
123 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
124 MethodDefinition methodDef = (MethodDefinition)def.getMethod();
125 BehaviorEntry behaviorEntry;
126 if (methodDef.isConstructor()) {
127 behaviorEntry = new ConstructorEntry(classEntry, methodDef.getSignature());
128 } else {
129 behaviorEntry = new MethodEntry(classEntry, methodDef.getName(), methodDef.getSignature());
130 }
131 ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName());
132 index.addDeclaration(node.getNameToken(), argumentEntry);
133
134 return recurse(node, index);
135 }
136
137 @Override
138 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
139 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
140 if (ref != null) {
141 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
142 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName());
143 index.addReference(node.getIdentifierToken(), fieldEntry, m_behaviorEntry);
144 }
145
146 return recurse(node, index);
147 }
148
149 @Override
150 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
151 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
152 if (ref != null) {
153 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
154 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, ref.getSignature());
155 if (node.getType() instanceof SimpleType) {
156 SimpleType simpleTypeNode = (SimpleType)node.getType();
157 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, m_behaviorEntry);
158 }
159 }
160
161 return recurse(node, index);
162 }
163}
diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
new file mode 100644
index 0000000..7b902a9
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.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 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;
33
34public class SourceIndexClassVisitor extends SourceIndexVisitor {
35
36 private ClassEntry m_classEntry;
37
38 public SourceIndexClassVisitor(ClassEntry classEntry) {
39 m_classEntry = classEntry;
40 }
41
42 @Override
43 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
44 // is this this class, or a subtype?
45 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
46 ClassEntry classEntry = new ClassEntry(def.getInternalName());
47 if (!classEntry.equals(m_classEntry)) {
48 // it's a sub-type, recurse
49 index.addDeclaration(node.getNameToken(), classEntry);
50 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
51 }
52
53 return recurse(node, index);
54 }
55
56 @Override
57 public Void visitSimpleType(SimpleType node, SourceIndex index) {
58 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
59 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
60 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
61 index.addReference(node.getIdentifierToken(), classEntry, m_classEntry);
62 }
63
64 return recurse(node, index);
65 }
66
67 @Override
68 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
69 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
70 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
71 BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(classEntry, def.getName(), def.getSignature());
72 AstNode tokenNode = node.getNameToken();
73 if (behaviorEntry instanceof ConstructorEntry) {
74 ConstructorEntry constructorEntry = (ConstructorEntry)behaviorEntry;
75 if (constructorEntry.isStatic()) {
76 tokenNode = node.getModifiers().firstOrNullObject();
77 }
78 }
79 index.addDeclaration(tokenNode, behaviorEntry);
80 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index);
81 }
82
83 @Override
84 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
85 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
86 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
87 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, def.getSignature());
88 index.addDeclaration(node.getNameToken(), constructorEntry);
89 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index);
90 }
91
92 @Override
93 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
94 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
95 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
96 FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName());
97 assert (node.getVariables().size() == 1);
98 VariableInitializer variable = node.getVariables().firstOrNullObject();
99 index.addDeclaration(variable.getNameToken(), fieldEntry);
100
101 return recurse(node, index);
102 }
103
104 @Override
105 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
106 // treat enum declarations as field declarations
107 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
108 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
109 FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName());
110 index.addDeclaration(node.getNameToken(), fieldEntry);
111
112 return recurse(node, index);
113 }
114}
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}