summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/TranslationIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/analysis/TranslationIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java172
1 files changed, 130 insertions, 42 deletions
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java
index c14fd593..4a356eb6 100644
--- a/src/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/cuchaz/enigma/analysis/TranslationIndex.java
@@ -11,97 +11,185 @@
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.io.Serializable; 13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.List; 14import java.util.List;
16import java.util.Map; 15import java.util.Map;
17import java.util.Set; 16import java.util.Set;
18 17
19import javassist.bytecode.Descriptor; 18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
20 21
21import com.google.common.collect.HashMultimap; 22import com.google.common.collect.HashMultimap;
22import com.google.common.collect.Lists; 23import com.google.common.collect.Lists;
23import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
24import com.google.common.collect.Multimap; 25import com.google.common.collect.Multimap;
25 26
27import cuchaz.enigma.mapping.ArgumentEntry;
28import cuchaz.enigma.mapping.BehaviorEntry;
29import cuchaz.enigma.mapping.ClassEntry;
30import cuchaz.enigma.mapping.Entry;
31import cuchaz.enigma.mapping.FieldEntry;
32import cuchaz.enigma.mapping.JavassistUtil;
33import cuchaz.enigma.mapping.Translator;
34
26public class TranslationIndex implements Serializable { 35public class TranslationIndex implements Serializable {
27 36
28 private static final long serialVersionUID = 738687982126844179L; 37 private static final long serialVersionUID = 738687982126844179L;
29 38
30 private Map<String,String> m_superclasses; 39 private Map<ClassEntry,ClassEntry> m_superclasses;
31 private Multimap<String,String> m_fields; 40 private Multimap<ClassEntry,FieldEntry> m_fieldEntries;
41 private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries;
32 42
33 public TranslationIndex() { 43 public TranslationIndex() {
34 m_superclasses = Maps.newHashMap(); 44 m_superclasses = Maps.newHashMap();
35 m_fields = HashMultimap.create(); 45 m_fieldEntries = HashMultimap.create();
36 } 46 m_behaviorEntries = HashMultimap.create();
37
38 public TranslationIndex(TranslationIndex other) {
39 m_superclasses = Maps.newHashMap(other.m_superclasses);
40 m_fields = HashMultimap.create(other.m_fields);
41 } 47 }
42 48
43 public void addSuperclass(String className, String superclassName) { 49 public TranslationIndex(TranslationIndex other, Translator translator) {
44 className = Descriptor.toJvmName(className);
45 superclassName = Descriptor.toJvmName(superclassName);
46 50
47 if (className.equals(superclassName)) { 51 // translate the superclasses
48 throw new IllegalArgumentException("Class cannot be its own superclass! " + className); 52 m_superclasses = Maps.newHashMap();
53 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_superclasses.entrySet()) {
54 m_superclasses.put(
55 translator.translateEntry(mapEntry.getKey()),
56 translator.translateEntry(mapEntry.getValue())
57 );
49 } 58 }
50 59
51 if (!isJre(className) && !isJre(superclassName)) { 60 // translate the fields
52 m_superclasses.put(className, superclassName); 61 m_fieldEntries = HashMultimap.create();
62 for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) {
63 m_fieldEntries.put(
64 translator.translateEntry(mapEntry.getKey()),
65 translator.translateEntry(mapEntry.getValue())
66 );
67 }
68
69 m_behaviorEntries = HashMultimap.create();
70 for (Map.Entry<ClassEntry,BehaviorEntry> mapEntry : other.m_behaviorEntries.entries()) {
71 m_behaviorEntries.put(
72 translator.translateEntry(mapEntry.getKey()),
73 translator.translateEntry(mapEntry.getValue())
74 );
53 } 75 }
54 } 76 }
55 77
56 public void addField(String className, String fieldName) { 78 public void indexClass(CtClass c) {
57 m_fields.put(className, fieldName); 79
80 ClassEntry classEntry = JavassistUtil.getClassEntry(c);
81
82 // add the superclass
83 ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c);
84 if (!isJre(classEntry) && !isJre(superclassEntry)) {
85 m_superclasses.put(classEntry, superclassEntry);
86 }
87
88 // add fields
89 for (CtField field : c.getDeclaredFields()) {
90 FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field);
91 m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
92 }
93
94 // add behaviors
95 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
96 BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior);
97 m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
98 }
58 } 99 }
59 100
60 public void renameClasses(Map<String,String> renames) { 101 public void renameClasses(Map<String,String> renames) {
61 EntryRenamer.renameClassesInMap(renames, m_superclasses); 102 EntryRenamer.renameClassesInMap(renames, m_superclasses);
62 EntryRenamer.renameClassesInMultimap(renames, m_fields); 103 EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries);
104 EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries);
63 } 105 }
64 106
65 public String getSuperclassName(String className) { 107 public ClassEntry getSuperclass(ClassEntry classEntry) {
66 return m_superclasses.get(className); 108 return m_superclasses.get(classEntry);
67 } 109 }
68 110
69 public List<String> getAncestry(String className) { 111 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
70 List<String> ancestors = new ArrayList<String>(); 112 List<ClassEntry> ancestors = Lists.newArrayList();
71 while (className != null) { 113 while (classEntry != null) {
72 className = getSuperclassName(className); 114 classEntry = getSuperclass(classEntry);
73 if (className != null) { 115 if (classEntry != null) {
74 ancestors.add(className); 116 ancestors.add(classEntry);
75 } 117 }
76 } 118 }
77 return ancestors; 119 return ancestors;
78 } 120 }
79 121
80 public List<String> getSubclassNames(String className) { 122 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
81 // linear search is fast enough for now 123 // linear search is fast enough for now
82 List<String> subclasses = Lists.newArrayList(); 124 List<ClassEntry> subclasses = Lists.newArrayList();
83 for (Map.Entry<String,String> entry : m_superclasses.entrySet()) { 125 for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) {
84 String subclass = entry.getKey(); 126 ClassEntry subclass = entry.getKey();
85 String superclass = entry.getValue(); 127 ClassEntry superclass = entry.getValue();
86 if (className.equals(superclass)) { 128 if (classEntry.equals(superclass)) {
87 subclasses.add(subclass); 129 subclasses.add(subclass);
88 } 130 }
89 } 131 }
90 return subclasses; 132 return subclasses;
91 } 133 }
92 134
93 public void getSubclassNamesRecursively(Set<String> out, String className) { 135 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
94 for (String subclassName : getSubclassNames(className)) { 136 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
95 out.add(subclassName); 137 out.add(subclassEntry);
96 getSubclassNamesRecursively(out, subclassName); 138 getSubclassesRecursively(out, subclassEntry);
139 }
140 }
141
142 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
143 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
144 out.add(subclassEntry.getName());
145 getSubclassNamesRecursively(out, subclassEntry);
97 } 146 }
98 } 147 }
99 148
100 public boolean containsField(String className, String fieldName) { 149 public boolean entryExists(Entry entry) {
101 return m_fields.containsEntry(className, fieldName); 150 if (entry instanceof FieldEntry) {
151 return fieldExists((FieldEntry)entry);
152 } else if (entry instanceof BehaviorEntry) {
153 return behaviorExists((BehaviorEntry)entry);
154 } else if (entry instanceof ArgumentEntry) {
155 return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry());
156 }
157 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
158 }
159
160 public boolean fieldExists(FieldEntry fieldEntry) {
161 return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
162 }
163
164 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
165 return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
166 }
167
168 public ClassEntry resolveEntryClass(Entry entry) {
169
170 if (entry instanceof ClassEntry) {
171 return (ClassEntry)entry;
172 }
173
174 // this entry could refer to a method on a class where the method is not actually implemented
175 // travel up the inheritance tree to find the closest implementation
176 while (!entryExists(entry)) {
177
178 // is there a parent class?
179 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
180 if (superclassEntry == null) {
181 // this is probably a method from a class in a library
182 // we can't trace the implementation up any higher unless we index the library
183 return null;
184 }
185
186 // move up to the parent class
187 entry = entry.cloneToNewClass(superclassEntry);
188 }
189 return entry.getClassEntry();
102 } 190 }
103 191
104 private boolean isJre(String className) { 192 private boolean isJre(ClassEntry classEntry) {
105 return className.startsWith("java/") || className.startsWith("javax/"); 193 return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax");
106 } 194 }
107} 195}