summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/TranslationIndex.java')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java543
1 files changed, 270 insertions, 273 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index d51131f..26be05b 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -8,291 +8,288 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
12 13
13import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
17
18import java.util.Collection;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import cuchaz.enigma.mapping.*; 18import cuchaz.enigma.mapping.*;
24import javassist.CtBehavior; 19import javassist.CtBehavior;
25import javassist.CtClass; 20import javassist.CtClass;
26import javassist.CtField; 21import javassist.CtField;
27import javassist.bytecode.Descriptor; 22import javassist.bytecode.Descriptor;
28 23
24import java.util.Collection;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28
29public class TranslationIndex { 29public class TranslationIndex {
30 30
31 private Map<ClassEntry, ClassEntry> superclasses; 31 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 32 private Multimap<ClassEntry, FieldEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 34 private Multimap<ClassEntry, ClassEntry> interfaces;
35 35
36 public TranslationIndex() { 36 public TranslationIndex() {
37 this.superclasses = Maps.newHashMap(); 37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 38 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 39 this.behaviorEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 40 this.interfaces = HashMultimap.create();
41 } 41 }
42 42
43 public TranslationIndex(TranslationIndex other, Translator translator) { 43 public TranslationIndex(TranslationIndex other, Translator translator) {
44 // translate the superclasses 44 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 45 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue()));
48 } 48 }
49 49
50 // translate the interfaces 50 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 51 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 53 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 54 translator.translateEntry(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 55 translator.translateEntry(mapEntry.getValue())
56 ); 56 );
57 } 57 }
58 58
59 // translate the fields 59 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 60 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 62 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 63 translator.translateEntry(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 64 translator.translateEntry(mapEntry.getValue())
65 ); 65 );
66 } 66 }
67 67
68 this.behaviorEntries = HashMultimap.create(); 68 this.behaviorEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) {
70 this.behaviorEntries.put( 70 this.behaviorEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 71 translator.translateEntry(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 72 translator.translateEntry(mapEntry.getValue())
73 ); 73 );
74 } 74 }
75 } 75 }
76 76
77 public void indexClass(CtClass c) { 77 public void indexClass(CtClass c) {
78 indexClass(c, true); 78 indexClass(c, true);
79 } 79 }
80 80
81 public void indexClass(CtClass c, boolean indexMembers) { 81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c); 82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 83 if (isJre(classEntry)) {
84 return; 84 return;
85 } 85 }
86 86
87 // add the superclass 87 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c);
89 if (superclassEntry != null) { 89 if (superclassEntry != null) {
90 this.superclasses.put(classEntry, superclassEntry); 90 this.superclasses.put(classEntry, superclassEntry);
91 } 91 }
92 92
93 // add the interfaces 93 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 94 for (String interfaceClassName : c.getClassFile().getInterfaces()) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName));
96 if (!isJre(interfaceClassEntry)) { 96 if (!isJre(interfaceClassEntry)) {
97 97
98 this.interfaces.put(classEntry, interfaceClassEntry); 98 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 99 }
100 } 100 }
101 101
102 if (indexMembers) { 102 if (indexMembers) {
103 // add fields 103 // add fields
104 for (CtField field : c.getDeclaredFields()) { 104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); 106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 } 107 }
108 108
109 // add behaviors 109 // add behaviors
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 110 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
113 } 113 }
114 } 114 }
115 } 115 }
116 116
117 public void renameClasses(Map<String, String> renames) { 117 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 118 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries);
121 } 121 }
122 122
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 123 public ClassEntry getSuperclass(ClassEntry classEntry) {
124 return this.superclasses.get(classEntry); 124 return this.superclasses.get(classEntry);
125 } 125 }
126 126
127 public List<ClassEntry> getAncestry(ClassEntry classEntry) { 127 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
128 List<ClassEntry> ancestors = Lists.newArrayList(); 128 List<ClassEntry> ancestors = Lists.newArrayList();
129 while (classEntry != null) { 129 while (classEntry != null) {
130 classEntry = getSuperclass(classEntry); 130 classEntry = getSuperclass(classEntry);
131 if (classEntry != null) { 131 if (classEntry != null) {
132 ancestors.add(classEntry); 132 ancestors.add(classEntry);
133 } 133 }
134 } 134 }
135 return ancestors; 135 return ancestors;
136 } 136 }
137 137
138 public List<ClassEntry> getSubclass(ClassEntry classEntry) { 138 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
139 // linear search is fast enough for now 139 // linear search is fast enough for now
140 List<ClassEntry> subclasses = Lists.newArrayList(); 140 List<ClassEntry> subclasses = Lists.newArrayList();
141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { 141 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
142 ClassEntry subclass = entry.getKey(); 142 ClassEntry subclass = entry.getKey();
143 ClassEntry superclass = entry.getValue(); 143 ClassEntry superclass = entry.getValue();
144 if (classEntry.equals(superclass)) { 144 if (classEntry.equals(superclass)) {
145 subclasses.add(subclass); 145 subclasses.add(subclass);
146 } 146 }
147 } 147 }
148 return subclasses; 148 return subclasses;
149 } 149 }
150 150
151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { 151 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
152 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 152 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
153 out.add(subclassEntry); 153 out.add(subclassEntry);
154 getSubclassesRecursively(out, subclassEntry); 154 getSubclassesRecursively(out, subclassEntry);
155 } 155 }
156 } 156 }
157 157
158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { 158 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
159 for (ClassEntry subclassEntry : getSubclass(classEntry)) { 159 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
160 out.add(subclassEntry.getName()); 160 out.add(subclassEntry.getName());
161 getSubclassNamesRecursively(out, subclassEntry); 161 getSubclassNamesRecursively(out, subclassEntry);
162 } 162 }
163 } 163 }
164 164
165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { 165 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() {
166 return this.interfaces.entries(); 166 return this.interfaces.entries();
167 } 167 }
168 168
169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { 169 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
170 return this.interfaces.get(classEntry); 170 return this.interfaces.get(classEntry);
171 } 171 }
172 172
173 public boolean isInterface(ClassEntry classEntry) { 173 public boolean isInterface(ClassEntry classEntry) {
174 return this.interfaces.containsValue(classEntry); 174 return this.interfaces.containsValue(classEntry);
175 } 175 }
176 176
177 public boolean entryExists(Entry entry) { 177 public boolean entryExists(Entry entry) {
178 if (entry instanceof FieldEntry) { 178 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 179 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 180 } else if (entry instanceof BehaviorEntry) {
181 return behaviorExists((BehaviorEntry) entry); 181 return behaviorExists((BehaviorEntry) entry);
182 } else if (entry instanceof ArgumentEntry) { 182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); 183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 184 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry());
186 } 186 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 188 }
189 189
190 public boolean fieldExists(FieldEntry fieldEntry) { 190 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
192 } 192 }
193 193
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 194 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
196 } 196 }
197 197
198 public ClassEntry resolveEntryClass(Entry entry) { 198 public ClassEntry resolveEntryClass(Entry entry) {
199 return resolveEntryClass(entry, false); 199 return resolveEntryClass(entry, false);
200 } 200 }
201 201
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 203 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 204 return (ClassEntry) entry;
205 } 205 }
206 206
207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); 207 ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild);
208 if (superclassEntry != null) { 208 if (superclassEntry != null) {
209 return superclassEntry; 209 return superclassEntry;
210 } 210 }
211 211
212 ClassEntry interfaceEntry = resolveInterface(entry); 212 ClassEntry interfaceEntry = resolveInterface(entry);
213 if (interfaceEntry != null) { 213 if (interfaceEntry != null) {
214 return interfaceEntry; 214 return interfaceEntry;
215 } 215 }
216 216
217 return null; 217 return null;
218 } 218 }
219 219
220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { 220 public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) {
221 221
222 // Default case 222 // Default case
223 if (!checkSuperclassBeforeChild) 223 if (!checkSuperclassBeforeChild)
224 return resolveSuperclass(entry); 224 return resolveSuperclass(entry);
225 225
226 // Save the original entry 226 // Save the original entry
227 Entry originalEntry = entry; 227 Entry originalEntry = entry;
228 228
229 // Get all possible superclasses and reverse the list 229 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry()));
231 231
232 boolean existInEntry = false; 232 boolean existInEntry = false;
233 233
234 for (ClassEntry classEntry : superclasses) 234 for (ClassEntry classEntry : superclasses) {
235 { 235 entry = entry.cloneToNewClass(classEntry);
236 entry = entry.cloneToNewClass(classEntry); 236 existInEntry = entryExists(entry);
237 existInEntry = entryExists(entry); 237
238 238 // Check for possible entry in interfaces of superclasses
239 // Check for possible entry in interfaces of superclasses 239 ClassEntry interfaceEntry = resolveInterface(entry);
240 ClassEntry interfaceEntry = resolveInterface(entry); 240 if (interfaceEntry != null)
241 if (interfaceEntry != null) 241 return interfaceEntry;
242 return interfaceEntry; 242 if (existInEntry)
243 if (existInEntry) 243 break;
244 break; 244 }
245 } 245
246 246 // Doesn't exists in superclasses? check the child or return null
247 // Doesn't exists in superclasses? check the child or return null 247 if (!existInEntry)
248 if (!existInEntry) 248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry();
249 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 249
250 250 return entry.getClassEntry();
251 return entry.getClassEntry(); 251 }
252 } 252
253 253 public ClassEntry resolveSuperclass(Entry entry) {
254 public ClassEntry resolveSuperclass(Entry entry) 254 // this entry could refer to a method on a class where the method is not actually implemented
255 { 255 // travel up the inheritance tree to find the closest implementation
256 // this entry could refer to a method on a class where the method is not actually implemented 256
257 // travel up the inheritance tree to find the closest implementation 257 while (!entryExists(entry)) {
258 258 // is there a parent class?
259 while (!entryExists(entry)) { 259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
260 // is there a parent class? 260 if (superclassEntry == null) {
261 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 261 // this is probably a method from a class in a library
262 if (superclassEntry == null) { 262 // we can't trace the implementation up any higher unless we index the library
263 // this is probably a method from a class in a library 263 return null;
264 // we can't trace the implementation up any higher unless we index the library 264 }
265 return null; 265
266 } 266 // move up to the parent class
267 267 entry = entry.cloneToNewClass(superclassEntry);
268 // move up to the parent class 268 }
269 entry = entry.cloneToNewClass(superclassEntry); 269 return entry.getClassEntry();
270 } 270 }
271 return entry.getClassEntry(); 271
272 } 272 public ClassEntry resolveInterface(Entry entry) {
273 273 // the interfaces for any class is a forest
274 public ClassEntry resolveInterface(Entry entry) { 274 // so let's look at all the trees
275 // the interfaces for any class is a forest 275
276 // so let's look at all the trees 276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) {
277 277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 278 if (subInterface != null && !subInterface.isEmpty()) {
279 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry));
280 if (subInterface != null && !subInterface.isEmpty()) 280 if (result != null)
281 { 281 return result;
282 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 282 }
283 if (result != null) 283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry));
284 return result; 284 if (resolvedClassEntry != null) {
285 } 285 return resolvedClassEntry;
286 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 286 }
287 if (resolvedClassEntry != null) { 287 }
288 return resolvedClassEntry; 288 return null;
289 } 289 }
290 } 290
291 return null; 291 private boolean isJre(ClassEntry classEntry) {
292 } 292 String packageName = classEntry.getPackageName();
293 293 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
294 private boolean isJre(ClassEntry classEntry) { 294 }
295 String packageName = classEntry.getPackageName();
296 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
297 }
298} 295}