summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/JarIndex.java
diff options
context:
space:
mode:
authorGravatar Gegy2019-01-24 14:48:32 +0200
committerGravatar Adrian Siekierka2019-01-24 13:48:32 +0100
commit00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch)
tree6f9e4c24dbcc6d118fceec56adf7bf9d747a485c /src/main/java/cuchaz/enigma/analysis/JarIndex.java
parentmark as 0.13.0-SNAPSHOT for preliminary development (diff)
downloadenigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.gz
enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.xz
enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.zip
[WIP] Mapping rework (#91)
* Move packages * Mapping & entry refactor: first pass * Fix deobf -> obf tree remapping * Resolve various issues * Give all entries the potential for parents and treat inner classes as children * Deobf UI tree elements * Tests pass * Sort mapping output * Fix delta tracking * Index separation and first pass for #97 * Keep track of remapped jar index * Fix child entries not being remapped * Drop non-root entries * Track dropped mappings * Fix enigma mapping ordering * EntryTreeNode interface * Small tweaks * Naive full index remap on rename * Entries can resolve to more than one root entry * Support alternative resolution strategies * Bridge method resolution * Tests pass * Fix mappings being used where there are none * Fix methods with different descriptors being considered unique. closes #89
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/JarIndex.java')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java583
1 files changed, 0 insertions, 583 deletions
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}