summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar jeff2015-01-19 22:22:57 -0500
committerGravatar jeff2015-01-19 22:22:57 -0500
commit2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c (patch)
tree0e9c23d6838d3e0299831dbc24b6d736c268cd8b /src/cuchaz/enigma/analysis
parentadded inverse operation for moving classes out of the default package (diff)
downloadenigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.gz
enigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.xz
enigma-fork-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.zip
solved tricky issue with incorrect translation of fields/methods referenced by a subclass instead of the declaring class
Diffstat (limited to 'src/cuchaz/enigma/analysis')
-rw-r--r--src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java65
-rw-r--r--src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java172
4 files changed, 155 insertions, 90 deletions
diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index b132305..3eaa391 100644
--- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -51,8 +51,8 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
51 public void load(TranslationIndex ancestries, boolean recurse) { 51 public void load(TranslationIndex ancestries, boolean recurse) {
52 // get all the child nodes 52 // get all the child nodes
53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
54 for (String subclassName : ancestries.getSubclassNames(m_obfClassName)) { 54 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) {
55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassName)); 55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName()));
56 } 56 }
57 57
58 // add them to this node 58 // add them to this node
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 4b03a33..c96d3bc 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -114,8 +114,8 @@ public class JarIndex {
114 // step 3: index extends, implements, fields, and methods 114 // step 3: index extends, implements, fields, and methods
115 for (CtClass c : JarClassIterator.classes(jar)) { 115 for (CtClass c : JarClassIterator.classes(jar)) {
116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); 116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
117 m_translationIndex.indexClass(c);
117 String className = Descriptor.toJvmName(c.getName()); 118 String className = Descriptor.toJvmName(c.getName());
118 m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass()));
119 for (String interfaceName : c.getClassFile().getInterfaces()) { 119 for (String interfaceName : c.getClassFile().getInterfaces()) {
120 className = Descriptor.toJvmName(className); 120 className = Descriptor.toJvmName(className);
121 interfaceName = Descriptor.toJvmName(interfaceName); 121 interfaceName = Descriptor.toJvmName(interfaceName);
@@ -191,8 +191,6 @@ public class JarIndex {
191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); 191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); 192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
193 193
194 m_translationIndex.addField(className, field.getName());
195
196 // is the field a class type? 194 // is the field a class type?
197 if (field.getSignature().startsWith("L")) { 195 if (field.getSignature().startsWith("L")) {
198 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); 196 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
@@ -230,13 +228,12 @@ public class JarIndex {
230 behavior.instrument(new ExprEditor() { 228 behavior.instrument(new ExprEditor() {
231 @Override 229 @Override
232 public void edit(MethodCall call) { 230 public void edit(MethodCall call) {
233 String className = Descriptor.toJvmName(call.getClassName());
234 MethodEntry calledMethodEntry = new MethodEntry( 231 MethodEntry calledMethodEntry = new MethodEntry(
235 new ClassEntry(className), 232 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
236 call.getMethodName(), 233 call.getMethodName(),
237 call.getSignature() 234 call.getSignature()
238 ); 235 );
239 ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry); 236 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry);
240 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { 237 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
241 calledMethodEntry = new MethodEntry( 238 calledMethodEntry = new MethodEntry(
242 resolvedClassEntry, 239 resolvedClassEntry,
@@ -254,12 +251,11 @@ public class JarIndex {
254 251
255 @Override 252 @Override
256 public void edit(FieldAccess call) { 253 public void edit(FieldAccess call) {
257 String className = Descriptor.toJvmName(call.getClassName());
258 FieldEntry calledFieldEntry = new FieldEntry( 254 FieldEntry calledFieldEntry = new FieldEntry(
259 new ClassEntry(className), 255 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
260 call.getFieldName() 256 call.getFieldName()
261 ); 257 );
262 ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry); 258 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry);
263 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { 259 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
264 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); 260 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
265 } 261 }
@@ -273,9 +269,8 @@ public class JarIndex {
273 269
274 @Override 270 @Override
275 public void edit(ConstructorCall call) { 271 public void edit(ConstructorCall call) {
276 String className = Descriptor.toJvmName(call.getClassName());
277 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 272 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
278 new ClassEntry(className), 273 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
279 call.getSignature() 274 call.getSignature()
280 ); 275 );
281 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 276 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -288,9 +283,8 @@ public class JarIndex {
288 283
289 @Override 284 @Override
290 public void edit(NewExpr call) { 285 public void edit(NewExpr call) {
291 String className = Descriptor.toJvmName(call.getClassName());
292 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 286 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
293 new ClassEntry(className), 287 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
294 call.getSignature() 288 call.getSignature()
295 ); 289 );
296 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 290 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -306,25 +300,6 @@ public class JarIndex {
306 } 300 }
307 } 301 }
308 302
309 public ClassEntry resolveEntryClass(Entry obfEntry) {
310
311 // this entry could refer to a method on a class where the method is not actually implemented
312 // travel up the inheritance tree to find the closest implementation
313 while (!containsObfEntry(obfEntry)) {
314 // is there a parent class?
315 String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName());
316 if (superclassName == null) {
317 // this is probably a method from a class in a library
318 // we can't trace the implementation up any higher unless we index the library
319 return null;
320 }
321
322 // move up to the parent class
323 obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName));
324 }
325 return obfEntry.getClassEntry();
326 }
327
328 private CtMethod getBridgedMethod(CtMethod method) { 303 private CtMethod getBridgedMethod(CtMethod method) {
329 304
330 // bridge methods just call another method, cast it to the return type, and return the result 305 // bridge methods just call another method, cast it to the return type, and return the result
@@ -402,9 +377,9 @@ public class JarIndex {
402 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { 377 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
403 378
404 // is the entry a superclass of the context? 379 // is the entry a superclass of the context?
405 String calledClassName = reference.entry.getClassName(); 380 ClassEntry calledClassEntry = reference.entry.getClassEntry();
406 String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName()); 381 ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry());
407 if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) { 382 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
408 // it's a super call, skip 383 // it's a super call, skip
409 continue; 384 continue;
410 } 385 }
@@ -599,7 +574,9 @@ public class JarIndex {
599 // get the root node 574 // get the root node
600 List<String> ancestry = Lists.newArrayList(); 575 List<String> ancestry = Lists.newArrayList();
601 ancestry.add(obfClassEntry.getName()); 576 ancestry.add(obfClassEntry.getName());
602 ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName())); 577 for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) {
578 ancestry.add(classEntry.getName());
579 }
603 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 580 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
604 deobfuscatingTranslator, 581 deobfuscatingTranslator,
605 ancestry.get(ancestry.size() - 1) 582 ancestry.get(ancestry.size() - 1)
@@ -625,21 +602,21 @@ public class JarIndex {
625 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 602 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
626 603
627 // travel to the ancestor implementation 604 // travel to the ancestor implementation
628 String baseImplementationClassName = obfMethodEntry.getClassName(); 605 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
629 for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) { 606 for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
630 MethodEntry ancestorMethodEntry = new MethodEntry( 607 MethodEntry ancestorMethodEntry = new MethodEntry(
631 new ClassEntry(ancestorClassName), 608 new ClassEntry(ancestorClassEntry),
632 obfMethodEntry.getName(), 609 obfMethodEntry.getName(),
633 obfMethodEntry.getSignature() 610 obfMethodEntry.getSignature()
634 ); 611 );
635 if (containsObfBehavior(ancestorMethodEntry)) { 612 if (containsObfBehavior(ancestorMethodEntry)) {
636 baseImplementationClassName = ancestorClassName; 613 baseImplementationClassEntry = ancestorClassEntry;
637 } 614 }
638 } 615 }
639 616
640 // make a root node at the base 617 // make a root node at the base
641 MethodEntry methodEntry = new MethodEntry( 618 MethodEntry methodEntry = new MethodEntry(
642 new ClassEntry(baseImplementationClassName), 619 baseImplementationClassEntry,
643 obfMethodEntry.getName(), 620 obfMethodEntry.getName(),
644 obfMethodEntry.getSignature() 621 obfMethodEntry.getSignature()
645 ); 622 );
@@ -781,8 +758,8 @@ public class JarIndex {
781 public Set<String> getInterfaces(String className) { 758 public Set<String> getInterfaces(String className) {
782 Set<String> interfaceNames = new HashSet<String>(); 759 Set<String> interfaceNames = new HashSet<String>();
783 interfaceNames.addAll(m_interfaces.get(className)); 760 interfaceNames.addAll(m_interfaces.get(className));
784 for (String ancestor : m_translationIndex.getAncestry(className)) { 761 for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) {
785 interfaceNames.addAll(m_interfaces.get(ancestor)); 762 interfaceNames.addAll(m_interfaces.get(ancestor.getName()));
786 } 763 }
787 return interfaceNames; 764 return interfaceNames;
788 } 765 }
@@ -795,7 +772,7 @@ public class JarIndex {
795 String interfaceName = entry.getValue(); 772 String interfaceName = entry.getValue();
796 if (interfaceName.equals(targetInterfaceName)) { 773 if (interfaceName.equals(targetInterfaceName)) {
797 classNames.add(className); 774 classNames.add(className);
798 m_translationIndex.getSubclassNamesRecursively(classNames, className); 775 m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className));
799 } 776 }
800 } 777 }
801 return classNames; 778 return classNames;
diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index eba8d87..8718220 100644
--- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -71,9 +71,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
71 public void load(JarIndex index, boolean recurse) { 71 public void load(JarIndex index, boolean recurse) {
72 // get all the child nodes 72 // get all the child nodes
73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
74 for (String subclassName : index.getTranslationIndex().getSubclassNames(m_entry.getClassName())) { 74 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) {
75 MethodEntry methodEntry = new MethodEntry( 75 MethodEntry methodEntry = new MethodEntry(
76 new ClassEntry(subclassName), 76 subclassEntry,
77 m_entry.getName(), 77 m_entry.getName(),
78 m_entry.getSignature() 78 m_entry.getSignature()
79 ); 79 );
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java
index c14fd59..4a356eb 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}