summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java35
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java32
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java30
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java34
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java167
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java39
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java29
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java87
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java583
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java48
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java46
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java67
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java10
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java53
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java15
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java19
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java275
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java109
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java)31
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java97
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java165
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java24
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java9
30 files changed, 963 insertions, 1375 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index 8181418..82ca669 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.bytecode.AccessFlags; 14import cuchaz.enigma.translation.representation.AccessFlags;
15 15
16import java.lang.reflect.Modifier; 16import java.lang.reflect.Modifier;
17 17
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index e876bb0..0fc44ca 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -12,26 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 20
19import javax.swing.tree.DefaultMutableTreeNode; 21import javax.swing.tree.DefaultMutableTreeNode;
22import java.util.Collection;
20import java.util.List; 23import java.util.List;
21 24
22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 25public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
23 26 private final Translator translator;
24 private final Translator deobfuscatingTranslator;
25 private final ClassEntry entry; 27 private final ClassEntry entry;
26 28
27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 29 public ClassImplementationsTreeNode(Translator translator, ClassEntry entry) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.translator = translator;
29 this.entry = entry; 31 this.entry = entry;
30 } 32 }
31 33
32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 34 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
33 // is this the node? 35 // is this the node?
34 if (node.entry.equals(entry.getOwnerClassEntry())) { 36 if (node.entry.equals(entry.getParent())) {
35 return node; 37 return node;
36 } 38 }
37 39
@@ -49,24 +51,19 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
49 return this.entry; 51 return this.entry;
50 } 52 }
51 53
52 public String getDeobfClassName() {
53 return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName();
54 }
55
56 @Override 54 @Override
57 public String toString() { 55 public String toString() {
58 String className = getDeobfClassName(); 56 return translator.translate(entry).toString();
59 if (className == null) {
60 className = this.entry.getClassName();
61 }
62 return className;
63 } 57 }
64 58
65 public void load(JarIndex index) { 59 public void load(JarIndex index) {
66 // get all method implementations 60 // get all method implementations
67 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); 61 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList();
68 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 62 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
69 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); 63
64 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry);
65 for (ClassEntry inheritor : inheritors) {
66 nodes.add(new ClassImplementationsTreeNode(translator, inheritor));
70 } 67 }
71 68
72 // add them to this node 69 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index b8ee17d..7904c5f 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -12,25 +12,25 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
17 18
18import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
19import java.util.List; 20import java.util.List;
20 21
21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
22 23 private final Translator translator;
23 private final Translator deobfuscatingTranslator;
24 private final ClassEntry obfClassEntry; 24 private final ClassEntry obfClassEntry;
25 25
26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator translator, String obfClassName) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.translator = translator;
28 this.obfClassEntry = new ClassEntry(obfClassName); 28 this.obfClassEntry = new ClassEntry(obfClassName);
29 } 29 }
30 30
31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
32 // is this the node? 32 // is this the node?
33 if (node.getObfClassName().equals(entry.getName())) { 33 if (node.getObfClassName().equals(entry.getFullName())) {
34 return node; 34 return node;
35 } 35 }
36 36
@@ -45,27 +45,19 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
45 } 45 }
46 46
47 public String getObfClassName() { 47 public String getObfClassName() {
48 return this.obfClassEntry.getClassName(); 48 return this.obfClassEntry.getFullName();
49 }
50
51 public String getDeobfClassName() {
52 return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName();
53 } 49 }
54 50
55 @Override 51 @Override
56 public String toString() { 52 public String toString() {
57 String deobfClassName = getDeobfClassName(); 53 return translator.translate(obfClassEntry).getFullName();
58 if (deobfClassName != null) {
59 return deobfClassName;
60 }
61 return this.obfClassEntry.getName();
62 } 54 }
63 55
64 public void load(TranslationIndex ancestries, boolean recurse) { 56 public void load(InheritanceIndex ancestries, boolean recurse) {
65 // get all the child nodes 57 // get all the child nodes
66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 58 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
67 for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) { 59 for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) {
68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); 60 nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName()));
69 } 61 }
70 62
71 // add them to this node 63 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
index ff5f2e9..90d8a6c 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
@@ -12,12 +12,12 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.mapping.entry.Entry; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.mapping.entry.MethodDefEntry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.mapping.entry.MethodEntry; 20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21 21
22import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
23import javax.swing.tree.TreeNode; 23import javax.swing.tree.TreeNode;
@@ -29,7 +29,6 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
29 private Translator deobfuscatingTranslator; 29 private Translator deobfuscatingTranslator;
30 private ClassEntry entry; 30 private ClassEntry entry;
31 private EntryReference<ClassEntry, MethodDefEntry> reference; 31 private EntryReference<ClassEntry, MethodDefEntry> reference;
32 private AccessFlags access;
33 32
34 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 33 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
35 this.deobfuscatingTranslator = deobfuscatingTranslator; 34 this.deobfuscatingTranslator = deobfuscatingTranslator;
@@ -37,12 +36,10 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
37 this.reference = null; 36 this.reference = null;
38 } 37 }
39 38
40 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, 39 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<ClassEntry, MethodDefEntry> reference) {
41 EntryReference<ClassEntry, MethodDefEntry> reference, AccessFlags access) {
42 this.deobfuscatingTranslator = deobfuscatingTranslator; 40 this.deobfuscatingTranslator = deobfuscatingTranslator;
43 this.entry = reference.entry; 41 this.entry = reference.entry;
44 this.reference = reference; 42 this.reference = reference;
45 this.access = access;
46 } 43 }
47 44
48 @Override 45 @Override
@@ -58,16 +55,17 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
58 @Override 55 @Override
59 public String toString() { 56 public String toString() {
60 if (this.reference != null) { 57 if (this.reference != null) {
61 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), 58 return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context));
62 this.access);
63 } 59 }
64 return this.deobfuscatingTranslator.getTranslatedClass(this.entry).getName(); 60 return this.deobfuscatingTranslator.translate(this.entry).getFullName();
65 } 61 }
66 62
67 public void load(JarIndex index, boolean recurse) { 63 public void load(JarIndex index, boolean recurse) {
64 ReferenceIndex referenceIndex = index.getReferenceIndex();
65
68 // get all the child nodes 66 // get all the child nodes
69 for (EntryReference<ClassEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry)) { 67 for (EntryReference<ClassEntry, MethodDefEntry> reference : referenceIndex.getReferencesToClass(this.entry)) {
70 add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 68 add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference));
71 } 69 }
72 70
73 if (recurse && this.children != null) { 71 if (recurse && this.children != null) {
@@ -76,7 +74,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
76 ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; 74 ClassReferenceTreeNode node = (ClassReferenceTreeNode) child;
77 75
78 // don't recurse into ancestor 76 // don't recurse into ancestor
79 Set<Entry> ancestors = Sets.newHashSet(); 77 Set<Entry<?>> ancestors = Sets.newHashSet();
80 TreeNode n = node; 78 TreeNode n = node;
81 while (n.getParent() != null) { 79 while (n.getParent() != null) {
82 n = n.getParent(); 80 n = n.getParent();
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index df36c23..e122210 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -11,15 +11,20 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry; 14import cuchaz.enigma.translation.Translatable;
15import cuchaz.enigma.mapping.entry.Entry; 15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.mapping.EntryResolver;
18import cuchaz.enigma.translation.mapping.EntryMap;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.Entry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import cuchaz.enigma.utils.Utils; 22import cuchaz.enigma.utils.Utils;
18 23
19import java.util.Arrays; 24import java.util.Arrays;
20import java.util.List; 25import java.util.List;
21 26
22public class EntryReference<E extends Entry, C extends Entry> { 27public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable {
23 28
24 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); 29 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static");
25 public E entry; 30 public E entry;
@@ -53,32 +58,24 @@ public class EntryReference<E extends Entry, C extends Entry> {
53 58
54 public ClassEntry getLocationClassEntry() { 59 public ClassEntry getLocationClassEntry() {
55 if (context != null) { 60 if (context != null) {
56 return context.getOwnerClassEntry(); 61 return context.getContainingClass();
57 } 62 }
58 return entry.getOwnerClassEntry(); 63 return entry.getContainingClass();
59 } 64 }
60 65
61 public boolean isNamed() { 66 public boolean isNamed() {
62 return this.sourceName; 67 return this.sourceName;
63 } 68 }
64 69
65 public Entry getNameableEntry() { 70 public Entry<?> getNameableEntry() {
66 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { 71 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) {
67 // renaming a constructor really means renaming the class 72 // renaming a constructor really means renaming the class
68 return entry.getOwnerClassEntry(); 73 return entry.getContainingClass();
69 } 74 }
70 return entry; 75 return entry;
71 } 76 }
72 77
73 public String getNameableName() { 78 public String getNameableName() {
74 if (getNameableEntry() instanceof ClassEntry) {
75 ClassEntry classEntry = (ClassEntry) getNameableEntry();
76 if (classEntry.isInnerClass()) {
77 // make sure we only rename the inner class name
78 return classEntry.getInnermostClassName();
79 }
80 }
81
82 return getNameableEntry().getName(); 79 return getNameableEntry().getName();
83 } 80 }
84 81
@@ -121,4 +118,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
121 } 118 }
122 return buf.toString(); 119 return buf.toString();
123 } 120 }
121
122 @Override
123 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
124 return new EntryReference<>(translator.translate(entry), translator.translate(context), this);
125 }
124} 126}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
deleted file mode 100644
index c474d68..0000000
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ /dev/null
@@ -1,167 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.mapping.*;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.AbstractMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24
25public class EntryRenamer {
26
27 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) {
28 List<T> entries = Lists.newArrayList();
29 for (T val : set) {
30 entries.add(renameClassesInThing(renames, val));
31 }
32 set.clear();
33 set.addAll(entries);
34 }
35
36 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) {
37 // for each key/value pair...
38 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
39 for (Map.Entry<Key, Val> entry : map.entrySet()) {
40 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
41 }
42 map.clear();
43 for (Map.Entry<Key, Val> entry : entriesToAdd) {
44 map.put(entry.getKey(), entry.getValue());
45 }
46 }
47
48 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) {
49 // for each key/value pair...
50 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
51 for (Map.Entry<Key, Val> entry : map.entries()) {
52 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
53 }
54 map.clear();
55 for (Map.Entry<Key, Val> entry : entriesToAdd) {
56 map.put(entry.getKey(), entry.getValue());
57 }
58 }
59
60 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) {
61 // for each key/value pair...
62 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
63 for (Map.Entry<Key, Val> entry : map.entries()) {
64 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
65 }
66 map.clear();
67 for (Map.Entry<Key, Val> entry : entriesToAdd) {
68 map.put(entry.getKey(), entry.getValue());
69 }
70 }
71
72 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) {
73 // for each key/value pair...
74 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
75 for (Map.Entry<Key, Val> entry : map.entrySet()) {
76 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
77 }
78 map.clear();
79 for (Map.Entry<Key, Val> entry : entriesToAdd) {
80 map.put(entry.getKey(), entry.getValue());
81 }
82 }
83
84 @SuppressWarnings("unchecked")
85 public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) {
86 if (thing instanceof MethodEntry) {
87 MethodEntry methodEntry = (MethodEntry) thing;
88 MethodEntry newMethodEntry = renames.get(methodEntry);
89 if (newMethodEntry != null) {
90 return (T) new MethodEntry(
91 methodEntry.getOwnerClassEntry(),
92 newMethodEntry.getName(),
93 methodEntry.getDesc()
94 );
95 }
96 return thing;
97 } else if (thing instanceof LocalVariableEntry) {
98 LocalVariableEntry variableEntry = (LocalVariableEntry) thing;
99 return (T) new LocalVariableEntry(
100 renameMethodsInThing(renames, variableEntry.getOwnerEntry()),
101 variableEntry.getIndex(),
102 variableEntry.getName(),
103 variableEntry.isParameter()
104 );
105 } else if (thing instanceof EntryReference) {
106 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
107 reference.entry = renameMethodsInThing(renames, reference.entry);
108 reference.context = renameMethodsInThing(renames, reference.context);
109 return thing;
110 }
111 return thing;
112 }
113
114 @SuppressWarnings("unchecked")
115 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) {
116 if (thing instanceof String) {
117 String stringEntry = (String) thing;
118 if (renames.containsKey(stringEntry)) {
119 return (T) renames.get(stringEntry);
120 }
121 } else if (thing instanceof ClassEntry) {
122 ClassEntry classEntry = (ClassEntry) thing;
123 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
124 } else if (thing instanceof FieldDefEntry) {
125 FieldDefEntry fieldEntry = (FieldDefEntry) thing;
126 return (T) new FieldDefEntry(
127 renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()),
128 fieldEntry.getName(),
129 renameClassesInThing(renames, fieldEntry.getDesc()),
130 renameClassesInThing(renames, fieldEntry.getSignature()),
131 fieldEntry.getAccess()
132 );
133 } else if (thing instanceof MethodDefEntry) {
134 MethodDefEntry methodEntry = (MethodDefEntry) thing;
135 return (T) new MethodDefEntry(
136 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
137 methodEntry.getName(),
138 renameClassesInThing(renames, methodEntry.getDesc()),
139 renameClassesInThing(renames, methodEntry.getSignature()),
140 methodEntry.getAccess()
141 );
142 } else if (thing instanceof MethodEntry) {
143 MethodEntry methodEntry = (MethodEntry) thing;
144 return (T) new MethodEntry(
145 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
146 methodEntry.getName(),
147 renameClassesInThing(renames, methodEntry.getDesc())
148 );
149 } else if (thing instanceof LocalVariableEntry) {
150 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
151 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName(), argumentEntry.isParameter());
152 } else if (thing instanceof EntryReference) {
153 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
154 reference.entry = renameClassesInThing(renames, reference.entry);
155 reference.context = renameClassesInThing(renames, reference.context);
156 return thing;
157 } else if (thing instanceof MethodDescriptor) {
158 return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
159 } else if (thing instanceof TypeDescriptor) {
160 return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
161 } else if (thing instanceof Signature) {
162 return (T) ((Signature) thing).remap(className -> renameClassesInThing(renames, className));
163 }
164
165 return thing;
166 }
167}
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 2318a2b..4beab7f 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -11,32 +11,31 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.bytecode.AccessFlags; 14import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.analysis.index.ReferenceIndex;
16import cuchaz.enigma.mapping.entry.FieldEntry; 16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.mapping.entry.MethodDefEntry; 17import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.mapping.entry.MethodEntry; 18import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 20
20import javax.swing.tree.DefaultMutableTreeNode; 21import javax.swing.tree.DefaultMutableTreeNode;
21 22
22public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { 23public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
23 24
24 private Translator deobfuscatingTranslator; 25 private final Translator translator;
25 private FieldEntry entry; 26 private FieldEntry entry;
26 private EntryReference<FieldEntry, MethodDefEntry> reference; 27 private EntryReference<FieldEntry, MethodDefEntry> reference;
27 private AccessFlags access;
28 28
29 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 29 public FieldReferenceTreeNode(Translator translator, FieldEntry entry) {
30 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.translator = translator;
31 this.entry = entry; 31 this.entry = entry;
32 this.reference = null; 32 this.reference = null;
33 } 33 }
34 34
35 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, AccessFlags access) { 35 private FieldReferenceTreeNode(Translator translator, EntryReference<FieldEntry, MethodDefEntry> reference) {
36 this.deobfuscatingTranslator = deobfuscatingTranslator; 36 this.translator = translator;
37 this.entry = reference.entry; 37 this.entry = reference.entry;
38 this.reference = reference; 38 this.reference = reference;
39 this.access = access;
40 } 39 }
41 40
42 @Override 41 @Override
@@ -52,27 +51,29 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
52 @Override 51 @Override
53 public String toString() { 52 public String toString() {
54 if (this.reference != null) { 53 if (this.reference != null) {
55 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access); 54 return String.format("%s", translator.translate(this.reference.context));
56 } 55 }
57 return deobfuscatingTranslator.getTranslatedField(entry).getName(); 56 return translator.translate(entry).toString();
58 } 57 }
59 58
60 public void load(JarIndex index, boolean recurse) { 59 public void load(JarIndex index, boolean recurse) {
60 ReferenceIndex referenceIndex = index.getReferenceIndex();
61
61 // get all the child nodes 62 // get all the child nodes
62 if (this.reference == null) { 63 if (this.reference == null) {
63 for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) { 64 for (EntryReference<FieldEntry, MethodDefEntry> reference : referenceIndex.getReferencesToField(this.entry)) {
64 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 65 add(new FieldReferenceTreeNode(translator, reference));
65 } 66 }
66 } else { 67 } else {
67 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.reference.context)) { 68 for (EntryReference<MethodEntry, MethodDefEntry> reference : referenceIndex.getReferencesToMethod(this.reference.context)) {
68 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.reference.context))); 69 add(new MethodReferenceTreeNode(translator, reference));
69 } 70 }
70 } 71 }
71 72
72 if (recurse && children != null) { 73 if (recurse && children != null) {
73 for (Object node : children) { 74 for (Object node : children) {
74 if (node instanceof MethodReferenceTreeNode) { 75 if (node instanceof MethodReferenceTreeNode) {
75 ((MethodReferenceTreeNode) node).load(index, true); 76 ((MethodReferenceTreeNode) node).load(index, true, false);
76 } else if (node instanceof FieldReferenceTreeNode) { 77 } else if (node instanceof FieldReferenceTreeNode) {
77 ((FieldReferenceTreeNode) node).load(index, true); 78 ((FieldReferenceTreeNode) node).load(index, true);
78 } 79 }
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
deleted file mode 100644
index b6ab2d5..0000000
--- a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
+++ /dev/null
@@ -1,29 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.entry.ClassEntry;
4import org.objectweb.asm.ClassVisitor;
5
6public class IndexInnerClassVisitor extends ClassVisitor {
7 private final JarIndex index;
8
9 public IndexInnerClassVisitor(JarIndex index, int api) {
10 super(api);
11 this.index = index;
12 }
13
14 public IndexInnerClassVisitor(JarIndex index, int api, ClassVisitor cv) {
15 super(api, cv);
16 this.index = index;
17 }
18
19 @Override
20 public void visitInnerClass(String name, String outerName, String innerName, int access) {
21 ClassEntry entry = new ClassEntry(name);
22 // Ignore anonymous classes
23 if (innerName != null && outerName != null) {
24 ClassEntry outerEntry = new ClassEntry(outerName);
25 index.indexInnerClass(entry, outerEntry);
26 }
27 super.visitInnerClass(name, outerName, innerName, access);
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
deleted file mode 100644
index f37f1e9..0000000
--- a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
+++ /dev/null
@@ -1,77 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.mapping.Signature;
6import cuchaz.enigma.mapping.entry.ClassEntry;
7import cuchaz.enigma.mapping.entry.MethodDefEntry;
8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.Handle;
10import org.objectweb.asm.MethodVisitor;
11import org.objectweb.asm.Opcodes;
12
13public class IndexReferenceVisitor extends ClassVisitor {
14 private final JarIndex index;
15 private ClassEntry classEntry;
16
17 public IndexReferenceVisitor(JarIndex index, int api) {
18 super(api);
19 this.index = index;
20 }
21
22 @Override
23 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 this.classEntry = new ClassEntry(name);
25 }
26
27 @Override
28 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
29 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
30 return new Method(this.index, entry, this.api);
31 }
32
33 private class Method extends MethodVisitor {
34 private final JarIndex index;
35 private final MethodDefEntry callerEntry;
36
37 public Method(JarIndex index, MethodDefEntry callerEntry, int api) {
38 super(api);
39 this.index = index;
40 this.callerEntry = callerEntry;
41 }
42
43 @Override
44 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
45 this.index.indexFieldAccess(callerEntry, owner, name, desc);
46 }
47
48 @Override
49 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
50 this.index.indexMethodCall(callerEntry, owner, name, desc);
51 }
52
53 @Override
54 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
55 for (Object bsmArg : bsmArgs){
56 if (bsmArg instanceof Handle){
57 Handle handle = (Handle)bsmArg;
58 switch (handle.getTag()){
59 case Opcodes.H_GETFIELD:
60 case Opcodes.H_GETSTATIC:
61 case Opcodes.H_PUTFIELD:
62 case Opcodes.H_PUTSTATIC:
63 this.index.indexFieldAccess(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
64 break;
65 case Opcodes.H_INVOKEINTERFACE:
66 case Opcodes.H_INVOKESPECIAL:
67 case Opcodes.H_INVOKESTATIC:
68 case Opcodes.H_INVOKEVIRTUAL:
69 case Opcodes.H_NEWINVOKESPECIAL:
70 this.index.indexMethodCall(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
71 break;
72 }
73 }
74 }
75 }
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
new file mode 100644
index 0000000..4ca7cd1
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
@@ -0,0 +1,87 @@
1package cuchaz.enigma.analysis;
2
3import com.google.common.collect.Lists;
4import cuchaz.enigma.analysis.index.EntryIndex;
5import cuchaz.enigma.analysis.index.JarIndex;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10
11import java.util.List;
12
13public class IndexTreeBuilder {
14 private final JarIndex index;
15
16 public IndexTreeBuilder(JarIndex index) {
17 this.index = index;
18 }
19
20 public ClassInheritanceTreeNode buildClassInheritance(Translator translator, ClassEntry obfClassEntry) {
21 // get the root node
22 List<String> ancestry = Lists.newArrayList();
23 ancestry.add(obfClassEntry.getFullName());
24 for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) {
25 ancestry.add(classEntry.getFullName());
26 }
27
28 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(translator, ancestry.get(ancestry.size() - 1));
29
30 // expand all children recursively
31 rootNode.load(index.getInheritanceIndex(), true);
32
33 return rootNode;
34 }
35
36 public ClassImplementationsTreeNode buildClassImplementations(Translator translator, ClassEntry obfClassEntry) {
37 if (index.getInheritanceIndex().isParent(obfClassEntry)) {
38 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(translator, obfClassEntry);
39 node.load(index);
40 return node;
41 }
42 return null;
43 }
44
45 public MethodInheritanceTreeNode buildMethodInheritance(Translator translator, MethodEntry obfMethodEntry) {
46 MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT);
47
48 // make a root node at the base
49 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
50 translator, resolvedEntry,
51 index.getEntryIndex().hasMethod(resolvedEntry)
52 );
53
54 // expand the full tree
55 rootNode.load(index, true);
56
57 return rootNode;
58 }
59
60 public List<MethodImplementationsTreeNode> buildMethodImplementations(Translator translator, MethodEntry obfMethodEntry) {
61 EntryIndex entryIndex = index.getEntryIndex();
62
63 List<MethodEntry> ancestorMethodEntries = Lists.newArrayList();
64
65 if (entryIndex.hasMethod(obfMethodEntry)) {
66 ancestorMethodEntries.add(obfMethodEntry);
67 }
68
69 for (ClassEntry ancestorEntry : index.getInheritanceIndex().getAncestors(obfMethodEntry.getParent())) {
70 MethodEntry ancestorMethod = obfMethodEntry.withParent(ancestorEntry);
71 if (entryIndex.hasMethod(ancestorMethod)) {
72 ancestorMethodEntries.add(ancestorMethod);
73 }
74 }
75
76 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
77 if (!ancestorMethodEntries.isEmpty()) {
78 for (MethodEntry interfaceMethodEntry : ancestorMethodEntries) {
79 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, interfaceMethodEntry);
80 node.load(index);
81 nodes.add(node);
82 }
83 }
84
85 return nodes;
86 }
87}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
deleted file mode 100644
index 361c8e7..0000000
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ /dev/null
@@ -1,583 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.*;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.*;
17import cuchaz.enigma.mapping.entry.*;
18import org.objectweb.asm.ClassReader;
19import org.objectweb.asm.ClassVisitor;
20import org.objectweb.asm.Opcodes;
21
22import java.util.*;
23
24public class JarIndex {
25
26 private final ReferencedEntryPool entryPool;
27
28 private Set<ClassEntry> obfClassEntries;
29 private TranslationIndex translationIndex;
30 private Map<Entry, AccessFlags> access;
31 private Multimap<ClassEntry, FieldDefEntry> fields;
32 private Multimap<ClassEntry, MethodDefEntry> methods;
33 private Multimap<String, MethodDefEntry> methodImplementations;
34 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodsReferencing;
35 private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> methodsReferencingClasses;
36 private Multimap<MethodEntry, MethodEntry> methodReferences;
37 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
38 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
39 private Map<ClassEntry, ClassEntry> outerClassesByInner;
40 private Map<MethodEntry, MethodEntry> bridgedMethods;
41 private Set<MethodEntry> syntheticMethods;
42
43 public JarIndex(ReferencedEntryPool entryPool) {
44 this.entryPool = entryPool;
45 this.obfClassEntries = Sets.newHashSet();
46 this.translationIndex = new TranslationIndex(entryPool);
47 this.access = Maps.newHashMap();
48 this.fields = HashMultimap.create();
49 this.methods = HashMultimap.create();
50 this.methodImplementations = HashMultimap.create();
51 this.methodsReferencingClasses = HashMultimap.create();
52 this.methodsReferencing = HashMultimap.create();
53 this.methodReferences = HashMultimap.create();
54 this.fieldReferences = HashMultimap.create();
55 this.innerClassesByOuter = HashMultimap.create();
56 this.outerClassesByInner = Maps.newHashMap();
57 this.bridgedMethods = Maps.newHashMap();
58 this.syntheticMethods = Sets.newHashSet();
59 }
60
61 public void indexJar(ParsedJar jar, boolean buildInnerClasses) {
62
63 // step 1: read the class names
64 obfClassEntries.addAll(jar.getClassEntries());
65
66 // step 2: index classes, fields, methods, interfaces
67 if (buildInnerClasses) {
68 // + step 5: index inner classes
69 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5, new IndexInnerClassVisitor(this, Opcodes.ASM5)), ClassReader.SKIP_CODE);
70 } else {
71 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
72 }
73
74 // step 3: index field, method, constructor references
75 jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES);
76
77 // step 4: index access and bridged methods
78 for (MethodDefEntry methodEntry : methods.values()) {
79 // look for access and bridged methods
80 MethodEntry accessedMethod = findAccessMethod(methodEntry);
81 if (accessedMethod != null) {
82 if (isBridgedMethod(accessedMethod, methodEntry)) {
83 this.bridgedMethods.put(methodEntry, accessedMethod);
84 }
85 }
86 }
87
88 if (buildInnerClasses) {
89 // step 6: update other indices with inner class info
90 Map<String, String> renames = Maps.newHashMap();
91 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) {
92 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName();
93 if (!innerClassEntry.getName().equals(newName)) {
94 // DEBUG
95 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName);
96 renames.put(innerClassEntry.getName(), newName);
97 }
98 }
99 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
100 this.translationIndex.renameClasses(renames);
101 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
102 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencingClasses);
103 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing);
104 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
105 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
106 EntryRenamer.renameClassesInMap(renames, this.access);
107 }
108 }
109
110 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
111 for (String interfaceName : interfaces) {
112 if (name.equals(interfaceName)) {
113 throw new IllegalArgumentException("Class cannot be its own interface! " + name);
114 }
115 }
116 ClassDefEntry entry = this.translationIndex.indexClass(access, name, signature, superName, interfaces);
117 this.access.put(entry, entry.getAccess());
118 return entry;
119 }
120
121 protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) {
122 FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
123 this.translationIndex.indexField(fieldEntry);
124 this.access.put(fieldEntry, fieldEntry.getAccess());
125 this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
126 }
127
128 protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) {
129 MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
130 this.translationIndex.indexMethod(methodEntry);
131 this.access.put(methodEntry, methodEntry.getAccess());
132 this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry);
133
134 if (new AccessFlags(access).isSynthetic()) {
135 syntheticMethods.add(methodEntry);
136 }
137
138 // we don't care about constructors here
139 if (!methodEntry.isConstructor()) {
140 // index implementation
141 this.methodImplementations.put(methodEntry.getClassName(), methodEntry);
142 }
143 }
144
145 protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) {
146 ClassEntry referencedClass = entryPool.getClass(owner);
147 MethodEntry referencedMethod = new MethodEntry(referencedClass, name, new MethodDescriptor(desc));
148 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod);
149 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
150 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
151 }
152 methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
153 if (referencedMethod.isConstructor()) {
154 methodsReferencingClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedMethod.getName(), callerEntry));
155 }
156 methodReferences.put(callerEntry, referencedMethod);
157 }
158
159 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
160 FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc));
161 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField);
162 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) {
163 referencedField = referencedField.updateOwnership(resolvedClassEntry);
164 }
165 fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry));
166 }
167
168 public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) {
169 this.innerClassesByOuter.put(outerEntry, innerEntry);
170 this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry);
171 }
172
173 private MethodEntry findAccessMethod(MethodDefEntry method) {
174
175 // we want to find all compiler-added methods that directly call another with no processing
176
177 // skip non-synthetic methods
178 if (!method.getAccess().isSynthetic()) {
179 return null;
180 }
181
182 // get all the methods that we call
183 final Collection<MethodEntry> referencedMethods = methodReferences.get(method);
184
185 // is there just one?
186 if (referencedMethods.size() != 1) {
187 return null;
188 }
189
190 return referencedMethods.stream().findFirst().orElse(null);
191 }
192
193 private boolean isBridgedMethod(MethodEntry called, MethodEntry access) {
194 // Bridged methods will always have the same name as the method they are calling
195 // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed)
196 if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) {
197 return false;
198 }
199
200 TypeDescriptor accessReturn = access.getDesc().getReturnDesc();
201 TypeDescriptor calledReturn = called.getDesc().getReturnDesc();
202 if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) {
203 return false;
204 }
205
206 // Bridged methods will never have the same type as what they are calling
207 if (accessReturn.equals(calledReturn)) {
208 return false;
209 }
210
211 String accessType = accessReturn.toString();
212
213 // If we're casting down from generic type to type-erased Object we're a bridge method
214 if (accessType.equals("Ljava/lang/Object;")) {
215 return true;
216 }
217
218 // Now we need to detect cases where we are being casted down to a higher type bound
219 List<ClassEntry> calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry());
220 return calledAncestry.contains(accessReturn.getTypeEntry());
221 }
222
223 public Set<ClassEntry> getObfClassEntries() {
224 return this.obfClassEntries;
225 }
226
227 public Collection<FieldDefEntry> getObfFieldEntries() {
228 return this.fields.values();
229 }
230
231 public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) {
232 return this.fields.get(classEntry);
233 }
234
235 public Collection<MethodDefEntry> getObfBehaviorEntries() {
236 return this.methods.values();
237 }
238
239 public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) {
240 return this.methods.get(classEntry);
241 }
242
243 public TranslationIndex getTranslationIndex() {
244 return this.translationIndex;
245 }
246
247 @Deprecated
248 public Access getAccess(Entry entry) {
249 AccessFlags flags = getAccessFlags(entry);
250 return flags != null ? Access.get(flags) : null;
251 }
252
253 public AccessFlags getAccessFlags(Entry entry) {
254 return this.access.get(entry);
255 }
256
257 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
258
259 // get the root node
260 List<String> ancestry = Lists.newArrayList();
261 ancestry.add(obfClassEntry.getName());
262 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) {
263 if (containsObfClass(classEntry)) {
264 ancestry.add(classEntry.getName());
265 }
266 }
267 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
268 deobfuscatingTranslator,
269 ancestry.get(ancestry.size() - 1)
270 );
271
272 // expand all children recursively
273 rootNode.load(this.translationIndex, true);
274
275 return rootNode;
276 }
277
278 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
279
280 // is this even an interface?
281 if (isInterface(obfClassEntry.getClassName())) {
282 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
283 node.load(this);
284 return node;
285 }
286 return null;
287 }
288
289 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
290 // travel to the ancestor implementation
291 LinkedList<ClassEntry> entries = new LinkedList<>();
292 entries.add(obfMethodEntry.getOwnerClassEntry());
293
294 // TODO: This could be optimized to not go through interfaces repeatedly...
295
296 ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry();
297
298 for (ClassEntry itf : getInterfaces(obfMethodEntry.getOwnerClassEntry().getClassName())) {
299 MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
300 if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) {
301 baseImplementationClassEntry = itf;
302 }
303 }
304
305 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(entries.remove())) {
306 MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
307 if (ancestorMethodEntry != null) {
308 if (containsObfMethod(ancestorMethodEntry)) {
309 baseImplementationClassEntry = ancestorClassEntry;
310 }
311
312 for (ClassEntry itf : getInterfaces(ancestorClassEntry.getClassName())) {
313 MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
314 if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) {
315 baseImplementationClassEntry = itf;
316 }
317 }
318 }
319 }
320
321 // make a root node at the base
322 MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
323 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
324 deobfuscatingTranslator,
325 methodEntry,
326 containsObfMethod(methodEntry)
327 );
328
329 // expand the full tree
330 rootNode.load(this, true);
331
332 return rootNode;
333 }
334
335 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
336
337 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList();
338
339 // is this method on an interface?
340 if (isInterface(obfMethodEntry.getClassName())) {
341 interfaceMethodEntries.add(obfMethodEntry);
342 } else {
343 // get the interface class
344 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
345
346 // is this method defined in this interface?
347 MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
348 if (methodInterface != null && containsObfMethod(methodInterface)) {
349 interfaceMethodEntries.add(methodInterface);
350 }
351 }
352 }
353
354 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
355 if (!interfaceMethodEntries.isEmpty()) {
356 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) {
357 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
358 node.load(this);
359 nodes.add(node);
360 }
361 }
362 return nodes;
363 }
364
365 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
366 AccessFlags flags = getAccessFlags(obfMethodEntry);
367 if (flags.isPrivate() || flags.isStatic()) {
368 return Collections.singleton(obfMethodEntry);
369 }
370
371 Set<MethodEntry> methodEntries = Sets.newHashSet();
372 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry));
373 return methodEntries;
374 }
375
376 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
377 MethodEntry methodEntry = node.getMethodEntry();
378 if (methodEntries.contains(methodEntry)) {
379 return;
380 }
381
382 if (containsObfMethod(methodEntry)) {
383 AccessFlags flags = getAccessFlags(methodEntry);
384 if (!flags.isPrivate() && !flags.isStatic()) {
385 // collect the entry
386 methodEntries.add(methodEntry);
387 }
388 }
389
390 // look at bridge methods!
391 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
392 while (bridgedMethod != null) {
393 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
394 bridgedMethod = getBridgedMethod(bridgedMethod);
395 }
396
397 // look at interface methods too
398 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) {
399 getRelatedMethodImplementations(methodEntries, implementationsNode);
400 }
401
402 // recurse
403 for (int i = 0; i < node.getChildCount(); i++) {
404 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
405 }
406 }
407
408 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
409 MethodEntry methodEntry = node.getMethodEntry();
410 if (containsObfMethod(methodEntry)) {
411 AccessFlags flags = getAccessFlags(methodEntry);
412 if (!flags.isPrivate() && !flags.isStatic()) {
413 // collect the entry
414 methodEntries.add(methodEntry);
415 }
416 }
417
418 // look at bridge methods!
419 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
420 while (bridgedMethod != null) {
421 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
422 bridgedMethod = getBridgedMethod(bridgedMethod);
423 }
424
425 // recurse
426 for (int i = 0; i < node.getChildCount(); i++) {
427 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
428 }
429 }
430
431 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) {
432 return this.fieldReferences.get(fieldEntry);
433 }
434
435 public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) {
436 // linear search is fast enough for now
437 Set<FieldEntry> fieldEntries = Sets.newHashSet();
438 for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) {
439 if (reference.context == methodEntry) {
440 fieldEntries.add(reference.entry);
441 }
442 }
443 return fieldEntries;
444 }
445
446 public Collection<EntryReference<ClassEntry, MethodDefEntry>> getMethodsReferencing(ClassEntry classEntry) {
447 return this.methodsReferencingClasses.get(classEntry);
448 }
449
450 @Deprecated
451 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry) {
452 return getMethodsReferencing(methodEntry, false);
453 }
454
455 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry, boolean recurse) {
456 if (!recurse) {
457 return this.methodsReferencing.get(methodEntry);
458 }
459
460 List<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>();
461 Set<MethodEntry> methodEntries = getRelatedMethodImplementations(methodEntry);
462 for (MethodEntry entry : methodEntries) {
463 references.addAll(getMethodsReferencing(entry, false));
464 }
465 return references;
466 }
467
468 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
469 return this.methodReferences.get(methodEntry);
470 }
471
472 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
473 return this.innerClassesByOuter.get(obfOuterClassEntry);
474 }
475
476 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) {
477 return this.outerClassesByInner.get(obfInnerClassEntry);
478 }
479
480 public boolean isSyntheticMethod(MethodEntry methodEntry) {
481 return this.syntheticMethods.contains(methodEntry);
482 }
483
484 public Set<ClassEntry> getInterfaces(String className) {
485 ClassEntry classEntry = entryPool.getClass(className);
486 Set<ClassEntry> interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry));
487 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
488 interfaces.addAll(this.translationIndex.getInterfaces(ancestor));
489 }
490 return interfaces;
491 }
492
493 public Set<String> getImplementingClasses(String targetInterfaceName) {
494
495 // linear search is fast enough for now
496 Set<String> classNames = Sets.newHashSet();
497 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) {
498 ClassEntry classEntry = entry.getKey();
499 ClassEntry interfaceEntry = entry.getValue();
500 if (interfaceEntry.getName().equals(targetInterfaceName)) {
501 String className = classEntry.getClassName();
502 classNames.add(className);
503 if (isInterface(className)) {
504 classNames.addAll(getImplementingClasses(className));
505 }
506
507 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry);
508 }
509 }
510 return classNames;
511 }
512
513 public boolean isInterface(String className) {
514 return this.translationIndex.isInterface(entryPool.getClass(className));
515 }
516
517 public boolean containsObfClass(ClassEntry obfClassEntry) {
518 return this.obfClassEntries.contains(obfClassEntry);
519 }
520
521 public boolean containsObfField(FieldEntry obfFieldEntry) {
522 return this.access.containsKey(obfFieldEntry);
523 }
524
525 public boolean containsObfMethod(MethodEntry obfMethodEntry) {
526 return this.access.containsKey(obfMethodEntry);
527 }
528
529 public boolean containsEntryWithSameName(Entry entry) {
530 for (Entry target : this.access.keySet())
531 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass()))
532 return true;
533 return false;
534 }
535
536 public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) {
537 // check the behavior
538 if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) {
539 return false;
540 }
541
542 return true;
543 }
544
545 public boolean containsObfEntry(Entry obfEntry) {
546 if (obfEntry instanceof ClassEntry) {
547 return containsObfClass((ClassEntry) obfEntry);
548 } else if (obfEntry instanceof FieldEntry) {
549 return containsObfField((FieldEntry) obfEntry);
550 } else if (obfEntry instanceof MethodEntry) {
551 return containsObfMethod((MethodEntry) obfEntry);
552 } else if (obfEntry instanceof LocalVariableEntry) {
553 return containsObfVariable((LocalVariableEntry) obfEntry);
554 } else {
555 throw new Error("Entry desc not supported: " + obfEntry.getClass().getName());
556 }
557 }
558
559 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) {
560 return this.bridgedMethods.get(bridgeMethodEntry);
561 }
562
563 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) {
564
565 // build class chain in inner-to-outer order
566 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry);
567 ClassEntry checkClassEntry = obfClassEntry;
568 while (true) {
569 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry);
570 if (obfOuterClassEntry != null) {
571 obfClassChain.add(obfOuterClassEntry);
572 checkClassEntry = obfOuterClassEntry;
573 } else {
574 break;
575 }
576 }
577
578 // switch to outer-to-inner order
579 Collections.reverse(obfClassChain);
580
581 return obfClassChain;
582 }
583}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 4b47c5f..e4b0304 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -12,24 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 21
19import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
23import java.util.Collection;
20import java.util.List; 24import java.util.List;
21 25
22public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 26public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
23 27
24 private Translator deobfuscatingTranslator; 28 private final Translator translator;
25 private MethodEntry entry; 29 private MethodEntry entry;
26 30
27 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 31 public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) {
32 this.translator = translator;
28 if (entry == null) { 33 if (entry == null) {
29 throw new IllegalArgumentException("Entry cannot be null!"); 34 throw new IllegalArgumentException("Entry cannot be null!");
30 } 35 }
31 36
32 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.entry = entry; 37 this.entry = entry;
34 } 38 }
35 39
@@ -53,35 +57,25 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
53 return this.entry; 57 return this.entry;
54 } 58 }
55 59
56 public String getDeobfClassName() {
57 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName();
58 }
59
60 public String getDeobfMethodName() {
61 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 }
63
64 @Override 60 @Override
65 public String toString() { 61 public String toString() {
66 String className = getDeobfClassName(); 62 MethodEntry translatedEntry = translator.translate(entry);
67 if (className == null) { 63 String className = translatedEntry.getParent().getFullName();
68 className = this.entry.getClassName(); 64 String methodName = translatedEntry.getName();
69 }
70
71 String methodName = getDeobfMethodName();
72 if (methodName == null) {
73 methodName = this.entry.getName();
74 }
75 return className + "." + methodName + "()"; 65 return className + "." + methodName + "()";
76 } 66 }
77 67
78 public void load(JarIndex index) { 68 public void load(JarIndex index) {
79 // get all method implementations 69 // get all method implementations
80 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 70 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
81 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 71 EntryIndex entryIndex = index.getEntryIndex();
82 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc()); 72 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
83 if (index.containsObfMethod(methodEntry)) { 73
84 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 74 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry.getParent());
75 for (ClassEntry inheritor : inheritors) {
76 MethodEntry methodEntry = entry.withParent(inheritor);
77 if (entryIndex.hasMethod(methodEntry)) {
78 nodes.add(new MethodImplementationsTreeNode(translator, methodEntry));
85 } 79 }
86 } 80 }
87 81
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index dc34197..f0fd1d2 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -12,21 +12,24 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 21
19import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List; 23import java.util.List;
21 24
22public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 25public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
23 26
24 private Translator deobfuscatingTranslator; 27 private final Translator translator;
25 private MethodEntry entry; 28 private MethodEntry entry;
26 private boolean isImplemented; 29 private boolean isImplemented;
27 30
28 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { 31 public MethodInheritanceTreeNode(Translator translator, MethodEntry entry, boolean isImplemented) {
29 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.translator = translator;
30 this.entry = entry; 33 this.entry = entry;
31 this.isImplemented = isImplemented; 34 this.isImplemented = isImplemented;
32 } 35 }
@@ -51,32 +54,19 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
51 return this.entry; 54 return this.entry;
52 } 55 }
53 56
54 public String getDeobfClassName() {
55 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName();
56 }
57
58 public String getDeobfMethodName() {
59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
60 }
61
62 public boolean isImplemented() { 57 public boolean isImplemented() {
63 return this.isImplemented; 58 return this.isImplemented;
64 } 59 }
65 60
66 @Override 61 @Override
67 public String toString() { 62 public String toString() {
68 String className = getDeobfClassName(); 63 MethodEntry translatedEntry = translator.translate(entry);
69 if (className == null) { 64 String className = translatedEntry.getContainingClass().getFullName();
70 className = this.entry.getClassName();
71 }
72 65
73 if (!this.isImplemented) { 66 if (!this.isImplemented) {
74 return className; 67 return className;
75 } else { 68 } else {
76 String methodName = getDeobfMethodName(); 69 String methodName = translatedEntry.getName();
77 if (methodName == null) {
78 methodName = this.entry.getName();
79 }
80 return className + "." + methodName + "()"; 70 return className + "." + methodName + "()";
81 } 71 }
82 } 72 }
@@ -84,14 +74,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
84 public void load(JarIndex index, boolean recurse) { 74 public void load(JarIndex index, boolean recurse) {
85 // get all the child nodes 75 // get all the child nodes
86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 76 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) { 77 EntryIndex entryIndex = index.getEntryIndex();
88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); 78 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
89 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry)));
90 }
91 79
92 for (ClassEntry subclassEntry : index.getTranslationIndex().getImplementers(this.entry.getOwnerClassEntry())) { 80 for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) {
93 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); 81 MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc());
94 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); 82 nodes.add(new MethodInheritanceTreeNode(translator, methodEntry, entryIndex.hasMethod(methodEntry)));
95 } 83 }
96 84
97 // add them to this node 85 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index ac05acd..8995eb5 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -12,36 +12,36 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.mapping.entry.Entry; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.mapping.entry.MethodDefEntry; 18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.mapping.entry.MethodEntry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
20 22
21import javax.swing.tree.DefaultMutableTreeNode; 23import javax.swing.tree.DefaultMutableTreeNode;
22import javax.swing.tree.TreeNode; 24import javax.swing.tree.TreeNode;
25import java.util.ArrayList;
26import java.util.Collection;
23import java.util.Set; 27import java.util.Set;
24 28
25public class MethodReferenceTreeNode extends DefaultMutableTreeNode 29public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
26 implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
27 30
28 private Translator deobfuscatingTranslator; 31 private final Translator translator;
29 private MethodEntry entry; 32 private MethodEntry entry;
30 private EntryReference<MethodEntry, MethodDefEntry> reference; 33 private EntryReference<MethodEntry, MethodDefEntry> reference;
31 private AccessFlags access;
32 34
33 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 35 public MethodReferenceTreeNode(Translator translator, MethodEntry entry) {
34 this.deobfuscatingTranslator = deobfuscatingTranslator; 36 this.translator = translator;
35 this.entry = entry; 37 this.entry = entry;
36 this.reference = null; 38 this.reference = null;
37 } 39 }
38 40
39 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, 41 public MethodReferenceTreeNode(Translator translator, EntryReference<MethodEntry, MethodDefEntry> reference) {
40 EntryReference<MethodEntry, MethodDefEntry> reference, AccessFlags access) { 42 this.translator = translator;
41 this.deobfuscatingTranslator = deobfuscatingTranslator;
42 this.entry = reference.entry; 43 this.entry = reference.entry;
43 this.reference = reference; 44 this.reference = reference;
44 this.access = access;
45 } 45 }
46 46
47 @Override 47 @Override
@@ -57,21 +57,17 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
57 @Override 57 @Override
58 public String toString() { 58 public String toString() {
59 if (this.reference != null) { 59 if (this.reference != null) {
60 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), 60 return String.format("%s", translator.translate(this.reference.context));
61 this.access);
62 } 61 }
63 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); 62 return translator.translate(this.entry).getName();
64 }
65
66 @Deprecated
67 public void load(JarIndex index, boolean recurse) {
68 load(index, recurse, false);
69 } 63 }
70 64
71 public void load(JarIndex index, boolean recurse, boolean recurseMethod) { 65 public void load(JarIndex index, boolean recurse, boolean recurseMethod) {
72 // get all the child nodes 66 // get all the child nodes
73 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry, recurseMethod)) { 67 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = getReferences(index, recurseMethod);
74 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 68
69 for (EntryReference<MethodEntry, MethodDefEntry> reference : references) {
70 add(new MethodReferenceTreeNode(translator, reference));
75 } 71 }
76 72
77 if (recurse && this.children != null) { 73 if (recurse && this.children != null) {
@@ -80,7 +76,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
80 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; 76 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child;
81 77
82 // don't recurse into ancestor 78 // don't recurse into ancestor
83 Set<Entry> ancestors = Sets.newHashSet(); 79 Set<Entry<?>> ancestors = Sets.newHashSet();
84 TreeNode n = node; 80 TreeNode n = node;
85 while (n.getParent() != null) { 81 while (n.getParent() != null) {
86 n = n.getParent(); 82 n = n.getParent();
@@ -92,9 +88,26 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
92 continue; 88 continue;
93 } 89 }
94 90
95 node.load(index, true); 91 node.load(index, true, false);
96 } 92 }
97 } 93 }
98 } 94 }
99 } 95 }
96
97 private Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferences(JarIndex index, boolean recurseMethod) {
98 ReferenceIndex referenceIndex = index.getReferenceIndex();
99
100 if (recurseMethod) {
101 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>();
102
103 EntryResolver entryResolver = index.getEntryResolver();
104 for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) {
105 references.addAll(referenceIndex.getReferencesToMethod(methodEntry));
106 }
107
108 return references;
109 } else {
110 return referenceIndex.getReferencesToMethod(entry);
111 }
112 }
100} 113}
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
index 86655d0..ad3aceb 100644
--- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -12,11 +12,12 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.io.ByteStreams; 14import com.google.common.io.ByteStreams;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import org.objectweb.asm.ClassReader; 16import org.objectweb.asm.ClassReader;
17import org.objectweb.asm.ClassVisitor; 17import org.objectweb.asm.ClassVisitor;
18import org.objectweb.asm.tree.ClassNode; 18import org.objectweb.asm.tree.ClassNode;
19 19
20import javax.annotation.Nullable;
20import java.io.BufferedInputStream; 21import java.io.BufferedInputStream;
21import java.io.IOException; 22import java.io.IOException;
22import java.io.InputStream; 23import java.io.InputStream;
@@ -100,9 +101,14 @@ public class ParsedJar {
100 return entries; 101 return entries;
101 } 102 }
102 103
104 @Nullable
103 public ClassNode getClassNode(String name) { 105 public ClassNode getClassNode(String name) {
104 return nodeCache.computeIfAbsent(name, (n) -> { 106 return nodeCache.computeIfAbsent(name, (n) -> {
105 ClassReader reader = new ClassReader(classBytes.get(name)); 107 byte[] bytes = classBytes.get(name);
108 if (bytes == null) {
109 return null;
110 }
111 ClassReader reader = new ClassReader(bytes);
106 ClassNode node = new ClassNode(); 112 ClassNode node = new ClassNode();
107 reader.accept(node, 0); 113 reader.accept(node, 0);
108 return node; 114 return node;
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index 3950d16..c0a3a75 100644
--- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -11,9 +11,9 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.entry.Entry; 14import cuchaz.enigma.translation.representation.entry.Entry;
15 15
16public interface ReferenceTreeNode<E extends Entry, C extends Entry> { 16public interface ReferenceTreeNode<E extends Entry<?>, C extends Entry<?>> {
17 E getEntry(); 17 E getEntry();
18 18
19 EntryReference<E, C> getReference(); 19 EntryReference<E, C> getReference();
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 3e0d66b..abdec92 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -11,17 +11,15 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.HashMultimap; 14import com.google.common.collect.*;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region; 15import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 16import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; 17import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
21import com.strobel.decompiler.languages.java.ast.Identifier; 18import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 19import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
23import cuchaz.enigma.mapping.entry.Entry; 20import cuchaz.enigma.translation.representation.entry.Entry;
24 21
22import javax.annotation.Nullable;
25import java.util.*; 23import java.util.*;
26import java.util.regex.Pattern; 24import java.util.regex.Pattern;
27 25
@@ -29,9 +27,9 @@ public class SourceIndex {
29 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); 27 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
30 28
31 private String source; 29 private String source;
32 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; 30 private TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
33 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; 31 private Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
34 private Map<Entry, Token> declarationToToken; 32 private Map<Entry<?>, Token> declarationToToken;
35 private List<Integer> lineOffsets; 33 private List<Integer> lineOffsets;
36 private boolean ignoreBadTokens; 34 private boolean ignoreBadTokens;
37 35
@@ -42,7 +40,7 @@ public class SourceIndex {
42 public SourceIndex(String source, boolean ignoreBadTokens) { 40 public SourceIndex(String source, boolean ignoreBadTokens) {
43 this.source = source; 41 this.source = source;
44 this.ignoreBadTokens = ignoreBadTokens; 42 this.ignoreBadTokens = ignoreBadTokens;
45 this.tokenToReference = Maps.newTreeMap(); 43 this.tokenToReference = new TreeMap<>();
46 this.referenceToTokens = HashMultimap.create(); 44 this.referenceToTokens = HashMultimap.create();
47 this.declarationToToken = Maps.newHashMap(); 45 this.declarationToToken = Maps.newHashMap();
48 calculateLineOffsets(); 46 calculateLineOffsets();
@@ -63,12 +61,12 @@ public class SourceIndex {
63 this.source = source; 61 this.source = source;
64 calculateLineOffsets(); 62 calculateLineOffsets();
65 63
66 for (Entry entry : Lists.newArrayList(declarationToToken.keySet())) { 64 for (Entry<?> entry : Lists.newArrayList(declarationToToken.keySet())) {
67 Token token = declarationToToken.get(entry); 65 Token token = declarationToToken.get(entry);
68 declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); 66 declarationToToken.put(entry, tokenMap.getOrDefault(token, token));
69 } 67 }
70 68
71 for (EntryReference<Entry, Entry> ref : referenceToTokens.keySet()) { 69 for (EntryReference<Entry<?>, Entry<?>> ref : referenceToTokens.keySet()) {
72 Collection<Token> oldTokens = referenceToTokens.get(ref); 70 Collection<Token> oldTokens = referenceToTokens.get(ref);
73 List<Token> newTokens = new ArrayList<>(oldTokens.size()); 71 List<Token> newTokens = new ArrayList<>(oldTokens.size());
74 72
@@ -79,7 +77,8 @@ public class SourceIndex {
79 referenceToTokens.replaceValues(ref, newTokens); 77 referenceToTokens.replaceValues(ref, newTokens);
80 } 78 }
81 79
82 Map<Token, EntryReference<Entry, Entry>> tokenToReferenceCopy = Maps.newHashMap(tokenToReference); 80 TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReferenceCopy = new TreeMap<>(tokenToReference);
81
83 tokenToReference.clear(); 82 tokenToReference.clear();
84 for (Token token : tokenToReferenceCopy.keySet()) { 83 for (Token token : tokenToReferenceCopy.keySet()) {
85 tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); 84 tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token));
@@ -112,9 +111,9 @@ public class SourceIndex {
112 return null; 111 return null;
113 } 112 }
114 113
115 if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){ 114 if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) {
116 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; 115 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
117 if (type != null){ 116 if (type != null) {
118 name = type.getName(); 117 name = type.getName();
119 token.end = token.start + name.length(); 118 token.end = token.start + name.length();
120 } 119 }
@@ -133,19 +132,19 @@ public class SourceIndex {
133 return token; 132 return token;
134 } 133 }
135 134
136 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { 135 public void addReference(AstNode node, Entry<?> deobfEntry, Entry<?> deobfContext) {
137 Token token = getToken(node); 136 Token token = getToken(node);
138 if (token != null) { 137 if (token != null) {
139 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); 138 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
140 this.tokenToReference.put(token, deobfReference); 139 this.tokenToReference.put(token, deobfReference);
141 this.referenceToTokens.put(deobfReference, token); 140 this.referenceToTokens.put(deobfReference, token);
142 } 141 }
143 } 142 }
144 143
145 public void addDeclaration(AstNode node, Entry deobfEntry) { 144 public void addDeclaration(AstNode node, Entry<?> deobfEntry) {
146 Token token = getToken(node); 145 Token token = getToken(node);
147 if (token != null) { 146 if (token != null) {
148 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); 147 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
149 this.tokenToReference.put(token, reference); 148 this.tokenToReference.put(token, reference);
150 this.referenceToTokens.put(reference, token); 149 this.referenceToTokens.put(reference, token);
151 this.declarationToToken.put(deobfEntry, token); 150 this.declarationToToken.put(deobfEntry, token);
@@ -160,22 +159,22 @@ public class SourceIndex {
160 return null; 159 return null;
161 } 160 }
162 161
163 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { 162 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
164 return this.referenceToTokens.get(deobfReference); 163 return this.referenceToTokens.get(deobfReference);
165 } 164 }
166 165
167 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 166 @Nullable
167 public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) {
168 if (token == null) { 168 if (token == null) {
169 return null; 169 return null;
170 } 170 }
171 return this.tokenToReference.get(token); 171 return this.tokenToReference.get(token);
172 } 172 }
173 173
174 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { 174 public void replaceDeobfReference(Token token, EntryReference<Entry<?>, Entry<?>> newDeobfReference) {
175 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); 175 EntryReference<Entry<?>, Entry<?>> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference);
176 this.tokenToReference.put(token, newDeobfReference); 176
177 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); 177 Collection<Token> tokens = this.referenceToTokens.removeAll(oldDeobfReferences);
178 this.referenceToTokens.removeAll(oldDeobfReference);
179 this.referenceToTokens.putAll(newDeobfReference, tokens); 178 this.referenceToTokens.putAll(newDeobfReference, tokens);
180 } 179 }
181 180
@@ -187,11 +186,11 @@ public class SourceIndex {
187 return this.declarationToToken.values(); 186 return this.declarationToToken.values();
188 } 187 }
189 188
190 public Iterable<Entry> declarations() { 189 public Iterable<Entry<?>> declarations() {
191 return this.declarationToToken.keySet(); 190 return this.declarationToToken.keySet();
192 } 191 }
193 192
194 public Token getDeclarationToken(Entry deobfEntry) { 193 public Token getDeclarationToken(Entry<?> deobfEntry) {
195 return this.declarationToToken.get(deobfEntry); 194 return this.declarationToToken.get(deobfEntry);
196 } 195 }
197 196
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index cad0857..486603c 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -17,9 +17,12 @@ import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference; 17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.bytecode.AccessFlags; 20import cuchaz.enigma.translation.representation.ProcyonEntryFactory;
21import cuchaz.enigma.mapping.Signature; 21import cuchaz.enigma.translation.representation.ReferencedEntryPool;
22import cuchaz.enigma.mapping.entry.*; 22import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
23import cuchaz.enigma.translation.representation.entry.ClassEntry;
24import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
25import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
23 26
24public class SourceIndexClassVisitor extends SourceIndexVisitor { 27public class SourceIndexClassVisitor extends SourceIndexVisitor {
25 private final ReferencedEntryPool entryPool; 28 private final ReferencedEntryPool entryPool;
@@ -37,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
37 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 40 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
38 // is this this class, or a subtype? 41 // is this this class, or a subtype?
39 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 42 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
40 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 43 ClassDefEntry classEntry = ClassDefEntry.parse(def);
41 if (!classEntry.equals(this.classEntry)) { 44 if (!classEntry.equals(this.classEntry)) {
42 // it's a subtype, recurse 45 // it's a subtype, recurse
43 index.addDeclaration(node.getNameToken(), classEntry); 46 index.addDeclaration(node.getNameToken(), classEntry);
@@ -68,7 +71,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
68 tokenNode = node.getModifiers().firstOrNullObject(); 71 tokenNode = node.getModifiers().firstOrNullObject();
69 } 72 }
70 index.addDeclaration(tokenNode, methodEntry); 73 index.addDeclaration(tokenNode, methodEntry);
71 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); 74 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index);
72 } 75 }
73 76
74 @Override 77 @Override
@@ -76,7 +79,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
76 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 79 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
77 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); 80 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
78 index.addDeclaration(node.getNameToken(), methodEntry); 81 index.addDeclaration(node.getNameToken(), methodEntry);
79 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); 82 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index);
80 } 83 }
81 84
82 @Override 85 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 139fcea..73db28f 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -17,8 +17,9 @@ import com.strobel.assembler.metadata.*;
17import com.strobel.decompiler.ast.Variable; 17import com.strobel.decompiler.ast.Variable;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.mapping.TypeDescriptor; 20import cuchaz.enigma.translation.representation.TypeDescriptor;
21import cuchaz.enigma.mapping.entry.*; 21import cuchaz.enigma.translation.representation.*;
22import cuchaz.enigma.translation.representation.entry.*;
22 23
23import java.lang.Error; 24import java.lang.Error;
24import java.util.HashMap; 25import java.util.HashMap;
@@ -26,19 +27,15 @@ import java.util.Map;
26 27
27public class SourceIndexMethodVisitor extends SourceIndexVisitor { 28public class SourceIndexMethodVisitor extends SourceIndexVisitor {
28 private final ReferencedEntryPool entryPool; 29 private final ReferencedEntryPool entryPool;
29 private final ProcyonEntryFactory entryFactory;
30 30
31 private final ClassDefEntry ownerEntry;
32 private final MethodDefEntry methodEntry; 31 private final MethodDefEntry methodEntry;
33 32
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 33 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 34 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>();
36 35
37 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, ClassDefEntry ownerEntry, MethodDefEntry methodEntry) { 36 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) {
38 super(entryPool); 37 super(entryPool);
39 this.entryPool = entryPool; 38 this.entryPool = entryPool;
40 this.entryFactory = new ProcyonEntryFactory(entryPool);
41 this.ownerEntry = ownerEntry;
42 this.methodEntry = methodEntry; 39 this.methodEntry = methodEntry;
43 } 40 }
44 41
@@ -86,7 +83,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
86 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 83 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
87 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); 84 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
88 if (fieldEntry == null) { 85 if (fieldEntry == null) {
89 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); 86 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
90 } 87 }
91 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); 88 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
92 } 89 }
@@ -128,7 +125,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
128 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 125 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
129 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); 126 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
130 if (fieldEntry == null) { 127 if (fieldEntry == null) {
131 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); 128 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
132 } 129 }
133 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); 130 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
134 } else 131 } else
@@ -144,7 +141,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
144 } 141 }
145 142
146 private void addDeclarationToUnmatched(String key, SourceIndex index) { 143 private void addDeclarationToUnmatched(String key, SourceIndex index) {
147 Entry entry = identifierEntryCache.get(key); 144 Entry<?> entry = identifierEntryCache.get(key);
148 145
149 // This cannot happened in theory 146 // This cannot happened in theory
150 if (entry == null) 147 if (entry == null)
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index e588d24..564830c 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -14,10 +14,8 @@ package cuchaz.enigma.analysis;
14import com.strobel.assembler.metadata.TypeDefinition; 14import com.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.*; 15import com.strobel.decompiler.languages.java.ast.*;
16import com.strobel.decompiler.patterns.Pattern; 16import com.strobel.decompiler.patterns.Pattern;
17import cuchaz.enigma.bytecode.AccessFlags; 17import cuchaz.enigma.translation.representation.ReferencedEntryPool;
18import cuchaz.enigma.mapping.Signature; 18import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.mapping.entry.ClassDefEntry;
20import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
21 19
22public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 20public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
23 private final ReferencedEntryPool entryPool; 21 private final ReferencedEntryPool entryPool;
@@ -29,7 +27,7 @@ public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
29 @Override 27 @Override
30 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 28 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
31 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 29 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
32 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 30 ClassDefEntry classEntry = ClassDefEntry.parse(def);
33 index.addDeclaration(node.getNameToken(), classEntry); 31 index.addDeclaration(node.getNameToken(), classEntry);
34 32
35 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); 33 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
deleted file mode 100644
index 984d84b..0000000
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ /dev/null
@@ -1,275 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.cache.Cache;
15import com.google.common.cache.CacheBuilder;
16import com.google.common.collect.HashMultimap;
17import com.google.common.collect.Lists;
18import com.google.common.collect.Maps;
19import com.google.common.collect.Multimap;
20import cuchaz.enigma.bytecode.AccessFlags;
21import cuchaz.enigma.mapping.*;
22import cuchaz.enigma.mapping.entry.*;
23
24import java.util.*;
25
26public class TranslationIndex {
27
28 private final ReferencedEntryPool entryPool;
29 private Map<ClassEntry, ClassEntry> superclasses;
30 private Map<Entry, DefEntry> defEntries = new HashMap<>();
31 private Multimap<ClassEntry, FieldDefEntry> fieldEntries;
32 private Multimap<ClassEntry, MethodDefEntry> methodEntries;
33 private Multimap<ClassEntry, ClassEntry> interfaces;
34
35 public TranslationIndex(ReferencedEntryPool entryPool) {
36 this.entryPool = entryPool;
37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create();
39 this.methodEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create();
41
42 for (FieldDefEntry entry : fieldEntries.values()) {
43 defEntries.put(entry, entry);
44 }
45
46 for (MethodDefEntry entry : methodEntries.values()) {
47 defEntries.put(entry, entry);
48 }
49 }
50
51 public TranslationIndex(TranslationIndex other, Translator translator) {
52 this.entryPool = other.entryPool;
53
54 // translate the superclasses
55 this.superclasses = Maps.newHashMap();
56 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
57 this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue()));
58 }
59
60 // translate the interfaces
61 this.interfaces = HashMultimap.create();
62 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
63 this.interfaces.put(
64 translator.getTranslatedClass(mapEntry.getKey()),
65 translator.getTranslatedClass(mapEntry.getValue())
66 );
67 }
68
69 // translate the fields
70 this.fieldEntries = HashMultimap.create();
71 for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) {
72 this.fieldEntries.put(
73 translator.getTranslatedClass(mapEntry.getKey()),
74 translator.getTranslatedFieldDef(mapEntry.getValue())
75 );
76 }
77
78 this.methodEntries = HashMultimap.create();
79 for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) {
80 this.methodEntries.put(
81 translator.getTranslatedClass(mapEntry.getKey()),
82 translator.getTranslatedMethodDef(mapEntry.getValue())
83 );
84 }
85
86 for (FieldDefEntry entry : fieldEntries.values()) {
87 defEntries.put(entry, entry);
88 }
89
90 for (MethodDefEntry entry : methodEntries.values()) {
91 defEntries.put(entry, entry);
92 }
93 }
94
95 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
96 ClassDefEntry classEntry = new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access));
97 if (isJre(classEntry)) {
98 return null;
99 }
100
101 // add the superclass
102 ClassEntry superclassEntry = entryPool.getClass(superName);
103 if (superclassEntry != null) {
104 this.superclasses.put(classEntry, superclassEntry);
105 }
106
107 // add the interfaces
108 for (String interfaceClassName : interfaces) {
109 ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName);
110 if (!isJre(interfaceClassEntry)) {
111 this.interfaces.put(classEntry, interfaceClassEntry);
112 }
113 }
114
115 return classEntry;
116 }
117
118 protected void indexField(FieldDefEntry fieldEntry) {
119 this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
120 this.defEntries.put(fieldEntry, fieldEntry);
121 }
122
123 protected void indexMethod(MethodDefEntry methodEntry) {
124 this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry);
125 this.defEntries.put(methodEntry, methodEntry);
126 }
127
128 public void renameClasses(Map<String, String> renames) {
129 EntryRenamer.renameClassesInMap(renames, this.superclasses);
130 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
131 EntryRenamer.renameClassesInMultimap(renames, this.methodEntries);
132
133 this.defEntries.clear();
134 for (FieldDefEntry entry : fieldEntries.values()) {
135 defEntries.put(entry, entry);
136 }
137
138 for (MethodDefEntry entry : methodEntries.values()) {
139 defEntries.put(entry, entry);
140 }
141 }
142
143 public ClassEntry getSuperclass(ClassEntry classEntry) {
144 return this.superclasses.get(classEntry);
145 }
146
147 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
148 List<ClassEntry> ancestors = Lists.newArrayList();
149 while (classEntry != null) {
150 classEntry = getSuperclass(classEntry);
151 if (classEntry != null) {
152 ancestors.add(classEntry);
153 }
154 }
155 return ancestors;
156 }
157
158 public List<ClassEntry> getImplementers(ClassEntry classEntry) {
159 // linear search is fast enough for now
160 List<ClassEntry> implementers = Lists.newArrayList();
161 for (ClassEntry itf : this.interfaces.keySet()) {
162 if (this.interfaces.containsEntry(itf, classEntry)) {
163 implementers.add(itf);
164 }
165 }
166 return implementers;
167 }
168
169 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
170 // linear search is fast enough for now
171 List<ClassEntry> subclasses = Lists.newArrayList();
172 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
173 ClassEntry subclass = entry.getKey();
174 ClassEntry superclass = entry.getValue();
175 if (classEntry.equals(superclass)) {
176 subclasses.add(subclass);
177 }
178 }
179 return subclasses;
180 }
181
182 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
183 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
184 out.add(subclassEntry);
185 getSubclassesRecursively(out, subclassEntry);
186 }
187 }
188
189 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
190 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
191 out.add(subclassEntry.getName());
192 getSubclassNamesRecursively(out, subclassEntry);
193 }
194 }
195
196 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() {
197 return this.interfaces.entries();
198 }
199
200 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
201 return this.interfaces.get(classEntry);
202 }
203
204 public boolean isInterface(ClassEntry classEntry) {
205 return this.interfaces.containsValue(classEntry);
206 }
207
208 public boolean entryExists(Entry entry) {
209 if (entry == null) {
210 return false;
211 }
212
213 if (entry instanceof FieldEntry) {
214 return fieldExists((FieldEntry) entry);
215 } else if (entry instanceof MethodEntry) {
216 return methodExists((MethodEntry) entry);
217 } else if (entry instanceof LocalVariableEntry) {
218 return methodExists(((LocalVariableEntry) entry).getOwnerEntry());
219 }
220 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
221 }
222
223 public boolean fieldExists(FieldEntry fieldEntry) {
224 return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry);
225 }
226
227 public boolean methodExists(MethodEntry methodEntry) {
228 return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry);
229 }
230
231 public ClassEntry resolveEntryOwner(Entry entry) {
232 if (entry instanceof ClassEntry) {
233 return (ClassEntry) entry;
234 }
235
236 if (entryExists(entry)) {
237 return entry.getOwnerClassEntry();
238 }
239
240 DefEntry def = defEntries.get(entry);
241 if (def != null && (def.getAccess().isPrivate())) {
242 return null;
243 }
244
245 // if we're protected/public/non-static, chances are we're somewhere down
246 LinkedList<ClassEntry> classEntries = new LinkedList<>();
247 classEntries.add(entry.getOwnerClassEntry());
248 while (!classEntries.isEmpty()) {
249 ClassEntry c = classEntries.remove();
250 Entry cEntry = entry.updateOwnership(c);
251
252 if (entryExists(cEntry)) {
253 def = defEntries.get(cEntry);
254 if (def == null || (!def.getAccess().isPrivate())) {
255 return cEntry.getOwnerClassEntry();
256 }
257 }
258
259 ClassEntry superC = getSuperclass(c);
260 if (superC != null) {
261 classEntries.add(superC);
262 }
263 if (entry instanceof MethodEntry) {
264 classEntries.addAll(getInterfaces(c));
265 }
266 }
267
268 return null;
269 }
270
271 private boolean isJre(ClassEntry classEntry) {
272 String packageName = classEntry.getPackageName();
273 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
274 }
275}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
new file mode 100644
index 0000000..e1903d9
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -0,0 +1,77 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.Maps;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryResolver;
6import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.entry.MethodEntry;
8
9import javax.annotation.Nullable;
10import java.util.Collection;
11import java.util.Map;
12
13public class BridgeMethodIndex implements JarIndexer, RemappableIndex {
14 private final EntryIndex entryIndex;
15 private final ReferenceIndex referenceIndex;
16
17 private Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap();
18
19 public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) {
20 this.entryIndex = entryIndex;
21 this.referenceIndex = referenceIndex;
22 }
23
24 @Override
25 public void remap(Translator translator) {
26 accessedToBridge = translator.translate(accessedToBridge);
27 }
28
29 @Override
30 public BridgeMethodIndex remapped(Translator translator) {
31 BridgeMethodIndex index = new BridgeMethodIndex(entryIndex, referenceIndex);
32 index.accessedToBridge = translator.translate(accessedToBridge);
33
34 return index;
35 }
36
37 @Override
38 public void processIndex(EntryResolver resolver) {
39 // look for access and bridged methods
40 for (MethodEntry methodEntry : entryIndex.getMethods()) {
41 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
42 if (access == null || !access.isSynthetic()) {
43 continue;
44 }
45
46 indexSyntheticMethod(methodEntry, access);
47 }
48 }
49
50 private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) {
51 if (access.isBridge()) {
52 MethodEntry accessedMethod = findAccessMethod(syntheticMethod);
53 if (accessedMethod != null) {
54 accessedToBridge.put(accessedMethod, syntheticMethod);
55 }
56 }
57 }
58
59 private MethodEntry findAccessMethod(MethodEntry method) {
60 // we want to find all compiler-added methods that directly call another with no processing
61
62 // get all the methods that we call
63 final Collection<MethodEntry> referencedMethods = referenceIndex.getMethodsReferencedBy(method);
64
65 // is there just one?
66 if (referencedMethods.size() != 1) {
67 return null;
68 }
69
70 return referencedMethods.stream().findFirst().orElse(null);
71 }
72
73 @Nullable
74 public MethodEntry getBridgeFromAccessed(MethodEntry entry) {
75 return accessedToBridge.get(entry);
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
new file mode 100644
index 0000000..55bfbc2
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
@@ -0,0 +1,109 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.entry.*;
6
7import javax.annotation.Nullable;
8import java.util.Collection;
9import java.util.HashMap;
10import java.util.Map;
11
12public class EntryIndex implements JarIndexer, RemappableIndex {
13 private Map<ClassEntry, AccessFlags> classes = new HashMap<>();
14 private Map<FieldEntry, AccessFlags> fields = new HashMap<>();
15 private Map<MethodEntry, AccessFlags> methods = new HashMap<>();
16
17 @Override
18 public void remap(Translator translator) {
19 classes = translator.translateKeys(classes);
20 fields = translator.translateKeys(fields);
21 methods = translator.translateKeys(methods);
22 }
23
24 @Override
25 public EntryIndex remapped(Translator translator) {
26 EntryIndex index = new EntryIndex();
27 index.classes = translator.translateKeys(classes);
28 index.fields = translator.translateKeys(fields);
29 index.methods = translator.translateKeys(methods);
30
31 return index;
32 }
33
34 @Override
35 public void indexClass(ClassDefEntry classEntry) {
36 classes.put(classEntry, classEntry.getAccess());
37 }
38
39 @Override
40 public void indexMethod(MethodDefEntry methodEntry) {
41 methods.put(methodEntry, methodEntry.getAccess());
42 }
43
44 @Override
45 public void indexField(FieldDefEntry fieldEntry) {
46 fields.put(fieldEntry, fieldEntry.getAccess());
47 }
48
49 public boolean hasClass(ClassEntry entry) {
50 return classes.containsKey(entry);
51 }
52
53 public boolean hasMethod(MethodEntry entry) {
54 return methods.containsKey(entry);
55 }
56
57 public boolean hasField(FieldEntry entry) {
58 return fields.containsKey(entry);
59 }
60
61 public boolean hasEntry(Entry<?> entry) {
62 if (entry instanceof ClassEntry) {
63 return hasClass((ClassEntry) entry);
64 } else if (entry instanceof MethodEntry) {
65 return hasMethod((MethodEntry) entry);
66 } else if (entry instanceof FieldEntry) {
67 return hasField((FieldEntry) entry);
68 } else if (entry instanceof LocalVariableEntry) {
69 return hasMethod(((LocalVariableEntry) entry).getParent());
70 }
71
72 return false;
73 }
74
75 @Nullable
76 public AccessFlags getMethodAccess(MethodEntry entry) {
77 return methods.get(entry);
78 }
79
80 @Nullable
81 public AccessFlags getFieldAccess(FieldEntry entry) {
82 return fields.get(entry);
83 }
84
85 @Nullable
86 public AccessFlags getEntryAccess(Entry<?> entry) {
87 if (entry instanceof MethodEntry) {
88 return getMethodAccess((MethodEntry) entry);
89 } else if (entry instanceof FieldEntry) {
90 return getFieldAccess((FieldEntry) entry);
91 } else if (entry instanceof LocalVariableEntry) {
92 return getMethodAccess(((LocalVariableEntry) entry).getParent());
93 }
94
95 return null;
96 }
97
98 public Collection<ClassEntry> getClasses() {
99 return classes.keySet();
100 }
101
102 public Collection<MethodEntry> getMethods() {
103 return methods.keySet();
104 }
105
106 public Collection<FieldEntry> getFields() {
107 return fields.keySet();
108 }
109}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
index 4d5e803..f9cb23c 100644
--- a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
@@ -1,43 +1,40 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.mapping.entry.ClassDefEntry; 3import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
4import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
5import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
4import org.objectweb.asm.ClassVisitor; 6import org.objectweb.asm.ClassVisitor;
5import org.objectweb.asm.FieldVisitor; 7import org.objectweb.asm.FieldVisitor;
6import org.objectweb.asm.MethodVisitor; 8import org.objectweb.asm.MethodVisitor;
7 9
8public class IndexClassVisitor extends ClassVisitor { 10public class IndexClassVisitor extends ClassVisitor {
9 private final JarIndex index; 11 private final JarIndexer indexer;
10 private ClassDefEntry classEntry; 12 private ClassDefEntry classEntry;
11 13
12 public IndexClassVisitor(JarIndex index, int api) { 14 public IndexClassVisitor(JarIndex indexer, int api) {
13 super(api); 15 super(api);
14 this.index = index; 16 this.indexer = indexer;
15 }
16
17 public IndexClassVisitor(JarIndex index, int api, ClassVisitor cv) {
18 super(api, cv);
19 this.index = index;
20 } 17 }
21 18
22 @Override 19 @Override
23 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 20 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 this.classEntry = this.index.indexClass(access, name, signature, superName, interfaces); 21 classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
22 indexer.indexClass(classEntry);
23
25 super.visit(version, access, name, signature, superName, interfaces); 24 super.visit(version, access, name, signature, superName, interfaces);
26 } 25 }
27 26
28 @Override 27 @Override
29 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 28 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
30 if (this.classEntry != null) { 29 indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature));
31 this.index.indexField(this.classEntry, access, name, desc, signature); 30
32 }
33 return super.visitField(access, name, desc, signature, value); 31 return super.visitField(access, name, desc, signature, value);
34 } 32 }
35 33
36 @Override 34 @Override
37 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 35 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
38 if (this.classEntry != null) { 36 indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature));
39 this.index.indexMethod(this.classEntry, access, name, desc, signature); 37
40 }
41 return super.visitMethod(access, name, desc, signature, exceptions); 38 return super.visitMethod(access, name, desc, signature, exceptions);
42 } 39 }
43} 40}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
new file mode 100644
index 0000000..ba5d3b6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.Signature;
6import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10import org.objectweb.asm.ClassVisitor;
11import org.objectweb.asm.Handle;
12import org.objectweb.asm.MethodVisitor;
13import org.objectweb.asm.Opcodes;
14
15public class IndexReferenceVisitor extends ClassVisitor {
16 private final JarIndexer indexer;
17 private ClassEntry classEntry;
18
19 public IndexReferenceVisitor(JarIndexer indexer, int api) {
20 super(api);
21 this.indexer = indexer;
22 }
23
24 @Override
25 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
26 this.classEntry = new ClassEntry(name);
27 }
28
29 @Override
30 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
32 return new Method(this.indexer, entry, this.api);
33 }
34
35 private static class Method extends MethodVisitor {
36 private final JarIndexer indexer;
37 private final MethodDefEntry callerEntry;
38
39 public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) {
40 super(api);
41 this.indexer = indexer;
42 this.callerEntry = callerEntry;
43 }
44
45 @Override
46 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
47 FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc);
48 this.indexer.indexFieldReference(callerEntry, fieldEntry);
49 }
50
51 @Override
52 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
53 MethodEntry methodEntry = MethodEntry.parse(owner, name, desc);
54 this.indexer.indexMethodReference(callerEntry, methodEntry);
55 }
56
57 @Override
58 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
59 for (Object bsmArg : bsmArgs) {
60 if (bsmArg instanceof Handle) {
61 Handle handle = (Handle) bsmArg;
62 switch (handle.getTag()) {
63 case Opcodes.H_GETFIELD:
64 case Opcodes.H_GETSTATIC:
65 case Opcodes.H_PUTFIELD:
66 case Opcodes.H_PUTSTATIC:
67 FieldEntry fieldEntry = FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
68 this.indexer.indexFieldReference(callerEntry, fieldEntry);
69 break;
70 case Opcodes.H_INVOKEINTERFACE:
71 case Opcodes.H_INVOKESPECIAL:
72 case Opcodes.H_INVOKESTATIC:
73 case Opcodes.H_INVOKEVIRTUAL:
74 case Opcodes.H_NEWINVOKESPECIAL:
75 MethodEntry methodEntry = MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
76 this.indexer.indexMethodReference(callerEntry, methodEntry);
77 break;
78 }
79 }
80 }
81 }
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
new file mode 100644
index 0000000..d165cc8
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -0,0 +1,97 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20
21import java.util.Collection;
22import java.util.LinkedList;
23import java.util.Set;
24
25public class InheritanceIndex implements JarIndexer, RemappableIndex {
26 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
27 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
28
29 @Override
30 public void remap(Translator translator) {
31 classChildren = translator.translate(classChildren);
32 classParents = translator.translate(classParents);
33 }
34
35 @Override
36 public InheritanceIndex remapped(Translator translator) {
37 InheritanceIndex index = new InheritanceIndex();
38 index.classParents = translator.translate(classParents);
39 index.classChildren = translator.translate(classChildren);
40
41 return index;
42 }
43
44 @Override
45 public void indexClass(ClassDefEntry classEntry) {
46 ClassEntry superClass = classEntry.getSuperClass();
47 if (superClass != null) {
48 indexParent(classEntry, superClass);
49 }
50
51 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
52 indexParent(classEntry, interfaceEntry);
53 }
54 }
55
56 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) {
57 if (childEntry.isJre() || parentEntry.isJre()) {
58 return;
59 }
60 classParents.put(childEntry, parentEntry);
61 classChildren.put(parentEntry, childEntry);
62 }
63
64 public Collection<ClassEntry> getParents(ClassEntry classEntry) {
65 return classParents.get(classEntry);
66 }
67
68 public Collection<ClassEntry> getChildren(ClassEntry classEntry) {
69 return classChildren.get(classEntry);
70 }
71
72 public Set<ClassEntry> getAncestors(ClassEntry classEntry) {
73 Set<ClassEntry> ancestors = Sets.newHashSet();
74
75 LinkedList<ClassEntry> ancestorQueue = new LinkedList<>();
76 ancestorQueue.push(classEntry);
77
78 while (!ancestorQueue.isEmpty()) {
79 ClassEntry ancestor = ancestorQueue.pop();
80 Collection<ClassEntry> parents = getParents(ancestor);
81
82 parents.forEach(ancestorQueue::push);
83 ancestors.addAll(parents);
84 }
85
86 return ancestors;
87 }
88
89 public boolean isParent(ClassEntry classEntry) {
90 return classChildren.containsKey(classEntry);
91 }
92
93 public boolean hasParents(ClassEntry classEntry) {
94 Collection<ClassEntry> parents = classParents.get(classEntry);
95 return parents != null && !parents.isEmpty();
96 }
97}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
new file mode 100644
index 0000000..0880244
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -0,0 +1,165 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.IndexEntryResolver;
20import cuchaz.enigma.translation.representation.entry.*;
21import org.objectweb.asm.ClassReader;
22import org.objectweb.asm.Opcodes;
23
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.function.Consumer;
27
28public class JarIndex implements JarIndexer, RemappableIndex {
29 private final EntryIndex entryIndex;
30 private final InheritanceIndex inheritanceIndex;
31 private final ReferenceIndex referenceIndex;
32 private final BridgeMethodIndex bridgeMethodIndex;
33 private final EntryResolver entryResolver;
34
35 private final Collection<JarIndexer> indexers;
36
37 private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create();
38
39 public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex) {
40 this.entryIndex = entryIndex;
41 this.inheritanceIndex = inheritanceIndex;
42 this.referenceIndex = referenceIndex;
43 this.bridgeMethodIndex = bridgeMethodIndex;
44 this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
45 this.entryResolver = new IndexEntryResolver(this);
46 }
47
48 public static JarIndex empty() {
49 EntryIndex entryIndex = new EntryIndex();
50 InheritanceIndex inheritanceIndex = new InheritanceIndex();
51 ReferenceIndex referenceIndex = new ReferenceIndex();
52 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex);
53 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
54 }
55
56 @Override
57 public void remap(Translator translator) {
58 entryIndex.remap(translator);
59 inheritanceIndex.remap(translator);
60 bridgeMethodIndex.remap(translator);
61 }
62
63 @Override
64 public JarIndex remapped(Translator translator) {
65 EntryIndex entryIndex = this.entryIndex.remapped(translator);
66 InheritanceIndex inheritanceIndex = this.inheritanceIndex.remapped(translator);
67 BridgeMethodIndex bridgeMethodIndex = this.bridgeMethodIndex.remapped(translator);
68
69 JarIndex remappedIndex = new JarIndex(entryIndex, inheritanceIndex, this.referenceIndex, bridgeMethodIndex);
70 remappedIndex.methodImplementations.putAll(methodImplementations);
71
72 return remappedIndex;
73 }
74
75 public void indexJar(ParsedJar jar, Consumer<String> progress) {
76 progress.accept("Indexing entries (1/3)");
77 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
78
79 progress.accept("Indexing entry references (2/3)");
80 jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES);
81
82 progress.accept("Processing index (3/3)");
83 processIndex(entryResolver);
84 }
85
86 @Override
87 public void processIndex(EntryResolver resolver) {
88 indexers.forEach(indexer -> indexer.processIndex(entryResolver));
89 }
90
91 @Override
92 public void indexClass(ClassDefEntry classEntry) {
93 if (classEntry.isJre()) {
94 return;
95 }
96
97 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
98 if (classEntry.equals(interfaceEntry)) {
99 throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry);
100 }
101 }
102
103 indexers.forEach(indexer -> indexer.indexClass(classEntry));
104 }
105
106 @Override
107 public void indexField(FieldDefEntry fieldEntry) {
108 if (fieldEntry.getParent().isJre()) {
109 return;
110 }
111
112 indexers.forEach(indexer -> indexer.indexField(fieldEntry));
113 }
114
115 @Override
116 public void indexMethod(MethodDefEntry methodEntry) {
117 if (methodEntry.getParent().isJre()) {
118 return;
119 }
120
121 indexers.forEach(indexer -> indexer.indexMethod(methodEntry));
122
123 if (!methodEntry.isConstructor()) {
124 methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry);
125 }
126 }
127
128 @Override
129 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
130 if (callerEntry.getParent().isJre()) {
131 return;
132 }
133
134 indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry));
135 }
136
137 @Override
138 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
139 if (callerEntry.getParent().isJre()) {
140 return;
141 }
142
143 indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry));
144 }
145
146 public EntryIndex getEntryIndex() {
147 return entryIndex;
148 }
149
150 public InheritanceIndex getInheritanceIndex() {
151 return this.inheritanceIndex;
152 }
153
154 public ReferenceIndex getReferenceIndex() {
155 return referenceIndex;
156 }
157
158 public BridgeMethodIndex getBridgeMethodIndex() {
159 return bridgeMethodIndex;
160 }
161
162 public EntryResolver getEntryResolver() {
163 return entryResolver;
164 }
165}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
new file mode 100644
index 0000000..a087e59
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
@@ -0,0 +1,24 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.mapping.EntryResolver;
4import cuchaz.enigma.translation.representation.entry.*;
5
6public interface JarIndexer {
7 default void indexClass(ClassDefEntry classEntry) {
8 }
9
10 default void indexField(FieldDefEntry fieldEntry) {
11 }
12
13 default void indexMethod(MethodDefEntry methodEntry) {
14 }
15
16 default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
17 }
18
19 default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
20 }
21
22 default void processIndex(EntryResolver resolver) {
23 }
24}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
new file mode 100644
index 0000000..ac11da4
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap;
5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.*;
9
10import java.util.Collection;
11import java.util.Map;
12
13public class ReferenceIndex implements JarIndexer {
14 private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create();
15
16 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create();
17 private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create();
18 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create();
19
20 @Override
21 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
22 referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
23 methodReferences.put(callerEntry, referencedEntry);
24
25 if (referencedEntry.isConstructor()) {
26 ClassEntry referencedClass = referencedEntry.getParent();
27 referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry));
28 }
29 }
30
31 @Override
32 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
33 referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
34 }
35
36 @Override
37 public void processIndex(EntryResolver resolver) {
38 methodReferences = resolveReferences(resolver, methodReferences);
39 referencesToMethods = resolveReferencesTo(resolver, referencesToMethods);
40 referencesToClasses = resolveReferencesTo(resolver, referencesToClasses);
41 referencesToFields = resolveReferencesTo(resolver, referencesToFields);
42 }
43
44 private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> resolveReferences(EntryResolver resolver, Multimap<K, V> multimap) {
45 Multimap<K, V> resolved = HashMultimap.create();
46 for (Map.Entry<K, V> entry : multimap.entries()) {
47 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
48 }
49 return resolved;
50 }
51
52 private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> resolveReferencesTo(EntryResolver resolver, Multimap<E, EntryReference<E, C>> multimap) {
53 Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create();
54 for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) {
55 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
56 }
57 return resolved;
58 }
59
60 private <E extends Entry<?>> E resolve(EntryResolver resolver, E entry) {
61 return resolver.resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST);
62 }
63
64 private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> resolve(EntryResolver resolver, EntryReference<E, C> reference) {
65 return resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
66 }
67
68 public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) {
69 return methodReferences.get(entry);
70 }
71
72 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) {
73 return referencesToFields.get(entry);
74 }
75
76 public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) {
77 return referencesToClasses.get(entry);
78 }
79
80 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) {
81 return referencesToMethods.get(entry);
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
new file mode 100644
index 0000000..537e772
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
@@ -0,0 +1,9 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4
5public interface RemappableIndex {
6 void remap(Translator translator);
7
8 RemappableIndex remapped(Translator translator);
9}