summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/JarIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/analysis/JarIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java820
1 files changed, 336 insertions, 484 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 0954564..4b03a33 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -53,8 +53,8 @@ import cuchaz.enigma.mapping.MethodEntry;
53import cuchaz.enigma.mapping.SignatureUpdater; 53import cuchaz.enigma.mapping.SignatureUpdater;
54import cuchaz.enigma.mapping.Translator; 54import cuchaz.enigma.mapping.Translator;
55 55
56public class JarIndex 56public class JarIndex {
57{ 57
58 private Set<ClassEntry> m_obfClassEntries; 58 private Set<ClassEntry> m_obfClassEntries;
59 private TranslationIndex m_translationIndex; 59 private TranslationIndex m_translationIndex;
60 private Multimap<String,String> m_interfaces; 60 private Multimap<String,String> m_interfaces;
@@ -68,8 +68,7 @@ public class JarIndex
68 private Map<String,BehaviorEntry> m_anonymousClasses; 68 private Map<String,BehaviorEntry> m_anonymousClasses;
69 private Map<MethodEntry,MethodEntry> m_bridgeMethods; 69 private Map<MethodEntry,MethodEntry> m_bridgeMethods;
70 70
71 public JarIndex( ) 71 public JarIndex() {
72 {
73 m_obfClassEntries = Sets.newHashSet(); 72 m_obfClassEntries = Sets.newHashSet();
74 m_translationIndex = new TranslationIndex(); 73 m_translationIndex = new TranslationIndex();
75 m_interfaces = HashMultimap.create(); 74 m_interfaces = HashMultimap.create();
@@ -84,192 +83,161 @@ public class JarIndex
84 m_bridgeMethods = Maps.newHashMap(); 83 m_bridgeMethods = Maps.newHashMap();
85 } 84 }
86 85
87 public void indexJar( JarFile jar, boolean buildInnerClasses ) 86 public void indexJar(JarFile jar, boolean buildInnerClasses) {
88 {
89 // step 1: read the class names 87 // step 1: read the class names
90 for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) 88 for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) {
91 { 89 if (classEntry.isInDefaultPackage()) {
92 if( classEntry.isInDefaultPackage() )
93 {
94 // move out of default package 90 // move out of default package
95 classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() ); 91 classEntry = new ClassEntry(Constants.NonePackage + "/" + classEntry.getName());
96 } 92 }
97 m_obfClassEntries.add( classEntry ); 93 m_obfClassEntries.add(classEntry);
98 } 94 }
99 95
100 // step 2: index field/method/constructor access 96 // step 2: index field/method/constructor access
101 for( CtClass c : JarClassIterator.classes( jar ) ) 97 for (CtClass c : JarClassIterator.classes(jar)) {
102 { 98 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
103 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); 99 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
104 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 100 for (CtField field : c.getDeclaredFields()) {
105 for( CtField field : c.getDeclaredFields() ) 101 FieldEntry fieldEntry = new FieldEntry(classEntry, field.getName());
106 { 102 m_access.put(fieldEntry, Access.get(field));
107 FieldEntry fieldEntry = new FieldEntry( classEntry, field.getName() );
108 m_access.put( fieldEntry, Access.get( field ) );
109 } 103 }
110 for( CtMethod method : c.getDeclaredMethods() ) 104 for (CtMethod method : c.getDeclaredMethods()) {
111 { 105 MethodEntry methodEntry = new MethodEntry(classEntry, method.getName(), method.getSignature());
112 MethodEntry methodEntry = new MethodEntry( classEntry, method.getName(), method.getSignature() ); 106 m_access.put(methodEntry, Access.get(method));
113 m_access.put( methodEntry, Access.get( method ) );
114 } 107 }
115 for( CtConstructor constructor : c.getDeclaredConstructors() ) 108 for (CtConstructor constructor : c.getDeclaredConstructors()) {
116 { 109 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, constructor.getSignature());
117 ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getSignature() ); 110 m_access.put(constructorEntry, Access.get(constructor));
118 m_access.put( constructorEntry, Access.get( constructor ) );
119 } 111 }
120 } 112 }
121 113
122 // step 3: index extends, implements, fields, and methods 114 // step 3: index extends, implements, fields, and methods
123 for( CtClass c : JarClassIterator.classes( jar ) ) 115 for (CtClass c : JarClassIterator.classes(jar)) {
124 { 116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
125 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); 117 String className = Descriptor.toJvmName(c.getName());
126 String className = Descriptor.toJvmName( c.getName() ); 118 m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass()));
127 m_translationIndex.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); 119 for (String interfaceName : c.getClassFile().getInterfaces()) {
128 for( String interfaceName : c.getClassFile().getInterfaces() ) 120 className = Descriptor.toJvmName(className);
129 { 121 interfaceName = Descriptor.toJvmName(interfaceName);
130 className = Descriptor.toJvmName( className ); 122 if (className.equals(interfaceName)) {
131 interfaceName = Descriptor.toJvmName( interfaceName ); 123 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
132 if( className.equals( interfaceName ) )
133 {
134 throw new IllegalArgumentException( "Class cannot be its own interface! " + className );
135 } 124 }
136 m_interfaces.put( className, interfaceName ); 125 m_interfaces.put(className, interfaceName);
137 } 126 }
138 for( CtField field : c.getDeclaredFields() ) 127 for (CtField field : c.getDeclaredFields()) {
139 { 128 indexField(field);
140 indexField( field );
141 } 129 }
142 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 130 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
143 { 131 indexBehavior(behavior);
144 indexBehavior( behavior );
145 } 132 }
146 } 133 }
147 134
148 // step 4: index field, method, constructor references 135 // step 4: index field, method, constructor references
149 for( CtClass c : JarClassIterator.classes( jar ) ) 136 for (CtClass c : JarClassIterator.classes(jar)) {
150 { 137 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
151 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); 138 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
152 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 139 indexBehaviorReferences(behavior);
153 {
154 indexBehaviorReferences( behavior );
155 } 140 }
156 } 141 }
157 142
158 if( buildInnerClasses ) 143 if (buildInnerClasses) {
159 {
160 // step 5: index inner classes and anonymous classes 144 // step 5: index inner classes and anonymous classes
161 for( CtClass c : JarClassIterator.classes( jar ) ) 145 for (CtClass c : JarClassIterator.classes(jar)) {
162 { 146 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
163 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); 147 String outerClassName = findOuterClass(c);
164 String outerClassName = findOuterClass( c ); 148 if (outerClassName != null) {
165 if( outerClassName != null )
166 {
167 String innerClassName = c.getSimpleName(); 149 String innerClassName = c.getSimpleName();
168 m_innerClasses.put( outerClassName, innerClassName ); 150 m_innerClasses.put(outerClassName, innerClassName);
169 boolean innerWasAdded = m_outerClasses.put( innerClassName, outerClassName ) == null; 151 boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null;
170 assert( innerWasAdded ); 152 assert (innerWasAdded);
171 153
172 BehaviorEntry enclosingBehavior = isAnonymousClass( c, outerClassName ); 154 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName);
173 if( enclosingBehavior != null ) 155 if (enclosingBehavior != null) {
174 { 156 m_anonymousClasses.put(innerClassName, enclosingBehavior);
175 m_anonymousClasses.put( innerClassName, enclosingBehavior );
176 157
177 // DEBUG 158 // DEBUG
178 //System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); 159 // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName );
179 } 160 } else {
180 else
181 {
182 // DEBUG 161 // DEBUG
183 //System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); 162 // System.out.println( "INNER: " + outerClassName + "$" + innerClassName );
184 } 163 }
185 } 164 }
186 } 165 }
187 166
188 // step 6: update other indices with inner class info 167 // step 6: update other indices with inner class info
189 Map<String,String> renames = Maps.newHashMap(); 168 Map<String,String> renames = Maps.newHashMap();
190 for( Map.Entry<String,String> entry : m_outerClasses.entrySet() ) 169 for (Map.Entry<String,String> entry : m_outerClasses.entrySet()) {
191 { 170 renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey());
192 renames.put( Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey() );
193 } 171 }
194 EntryRenamer.renameClassesInSet( renames, m_obfClassEntries ); 172 EntryRenamer.renameClassesInSet(renames, m_obfClassEntries);
195 m_translationIndex.renameClasses( renames ); 173 m_translationIndex.renameClasses(renames);
196 EntryRenamer.renameClassesInMultimap( renames, m_interfaces ); 174 EntryRenamer.renameClassesInMultimap(renames, m_interfaces);
197 EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations ); 175 EntryRenamer.renameClassesInMultimap(renames, m_methodImplementations);
198 EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); 176 EntryRenamer.renameClassesInMultimap(renames, m_behaviorReferences);
199 EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); 177 EntryRenamer.renameClassesInMultimap(renames, m_fieldReferences);
200 EntryRenamer.renameClassesInMap( renames, m_bridgeMethods ); 178 EntryRenamer.renameClassesInMap(renames, m_bridgeMethods);
201 EntryRenamer.renameClassesInMap( renames, m_access ); 179 EntryRenamer.renameClassesInMap(renames, m_access);
202 } 180 }
203 181
204 // step 6: update other indices with bridge method info 182 // step 6: update other indices with bridge method info
205 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations ); 183 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_methodImplementations);
206 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences ); 184 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_behaviorReferences);
207 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); 185 EntryRenamer.renameMethodsInMultimap(m_bridgeMethods, m_fieldReferences);
208 EntryRenamer.renameMethodsInMap( m_bridgeMethods, m_access ); 186 EntryRenamer.renameMethodsInMap(m_bridgeMethods, m_access);
209 } 187 }
210 188
211 private void indexField( CtField field ) 189 private void indexField(CtField field) {
212 {
213 // get the field entry 190 // get the field entry
214 String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); 191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
215 FieldEntry fieldEntry = new FieldEntry( new ClassEntry( className ), field.getName() ); 192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
216 193
217 m_translationIndex.addField( className, field.getName() ); 194 m_translationIndex.addField(className, field.getName());
218 195
219 // is the field a class type? 196 // is the field a class type?
220 if( field.getSignature().startsWith( "L" ) ) 197 if (field.getSignature().startsWith("L")) {
221 { 198 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
222 ClassEntry fieldTypeEntry = new ClassEntry( field.getSignature().substring( 1, field.getSignature().length() - 1 ) ); 199 m_fieldClasses.put(fieldEntry, fieldTypeEntry);
223 m_fieldClasses.put( fieldEntry, fieldTypeEntry );
224 } 200 }
225 } 201 }
226 202
227 private void indexBehavior( CtBehavior behavior ) 203 private void indexBehavior(CtBehavior behavior) {
228 {
229 // get the behavior entry 204 // get the behavior entry
230 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); 205 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
231 if( behaviorEntry instanceof MethodEntry ) 206 if (behaviorEntry instanceof MethodEntry) {
232 {
233 MethodEntry methodEntry = (MethodEntry)behaviorEntry; 207 MethodEntry methodEntry = (MethodEntry)behaviorEntry;
234 208
235 // index implementation 209 // index implementation
236 m_methodImplementations.put( behaviorEntry.getClassName(), methodEntry ); 210 m_methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
237 211
238 // look for bridge methods 212 // look for bridge methods
239 CtMethod bridgedMethod = getBridgedMethod( (CtMethod)behavior ); 213 CtMethod bridgedMethod = getBridgedMethod((CtMethod)behavior);
240 if( bridgedMethod != null ) 214 if (bridgedMethod != null) {
241 {
242 MethodEntry bridgedMethodEntry = new MethodEntry( 215 MethodEntry bridgedMethodEntry = new MethodEntry(
243 behaviorEntry.getClassEntry(), 216 behaviorEntry.getClassEntry(),
244 bridgedMethod.getName(), 217 bridgedMethod.getName(),
245 bridgedMethod.getSignature() 218 bridgedMethod.getSignature()
246 ); 219 );
247 m_bridgeMethods.put( bridgedMethodEntry, methodEntry ); 220 m_bridgeMethods.put(bridgedMethodEntry, methodEntry);
248 } 221 }
249 } 222 }
250 // looks like we don't care about constructors here 223 // looks like we don't care about constructors here
251 } 224 }
252 225
253 private void indexBehaviorReferences( CtBehavior behavior ) 226 private void indexBehaviorReferences(CtBehavior behavior) {
254 {
255 // index method calls 227 // index method calls
256 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create( behavior ); 228 final BehaviorEntry behaviorEntry = BehaviorEntryFactory.create(behavior);
257 try 229 try {
258 { 230 behavior.instrument(new ExprEditor() {
259 behavior.instrument( new ExprEditor( )
260 {
261 @Override 231 @Override
262 public void edit( MethodCall call ) 232 public void edit(MethodCall call) {
263 { 233 String className = Descriptor.toJvmName(call.getClassName());
264 String className = Descriptor.toJvmName( call.getClassName() );
265 MethodEntry calledMethodEntry = new MethodEntry( 234 MethodEntry calledMethodEntry = new MethodEntry(
266 new ClassEntry( className ), 235 new ClassEntry(className),
267 call.getMethodName(), 236 call.getMethodName(),
268 call.getSignature() 237 call.getSignature()
269 ); 238 );
270 ClassEntry resolvedClassEntry = resolveEntryClass( calledMethodEntry ); 239 ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry);
271 if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledMethodEntry.getClassEntry() ) ) 240 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
272 {
273 calledMethodEntry = new MethodEntry( 241 calledMethodEntry = new MethodEntry(
274 resolvedClassEntry, 242 resolvedClassEntry,
275 call.getMethodName(), 243 call.getMethodName(),
@@ -281,39 +249,33 @@ public class JarIndex
281 call.getMethodName(), 249 call.getMethodName(),
282 behaviorEntry 250 behaviorEntry
283 ); 251 );
284 m_behaviorReferences.put( calledMethodEntry, reference ); 252 m_behaviorReferences.put(calledMethodEntry, reference);
285 } 253 }
286 254
287 @Override 255 @Override
288 public void edit( FieldAccess call ) 256 public void edit(FieldAccess call) {
289 { 257 String className = Descriptor.toJvmName(call.getClassName());
290 String className = Descriptor.toJvmName( call.getClassName() );
291 FieldEntry calledFieldEntry = new FieldEntry( 258 FieldEntry calledFieldEntry = new FieldEntry(
292 new ClassEntry( className ), 259 new ClassEntry(className),
293 call.getFieldName() 260 call.getFieldName()
294 ); 261 );
295 ClassEntry resolvedClassEntry = resolveEntryClass( calledFieldEntry ); 262 ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry);
296 if( resolvedClassEntry != null && !resolvedClassEntry.equals( calledFieldEntry.getClassEntry() ) ) 263 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
297 { 264 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
298 calledFieldEntry = new FieldEntry(
299 resolvedClassEntry,
300 call.getFieldName()
301 );
302 } 265 }
303 EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>( 266 EntryReference<FieldEntry,BehaviorEntry> reference = new EntryReference<FieldEntry,BehaviorEntry>(
304 calledFieldEntry, 267 calledFieldEntry,
305 call.getFieldName(), 268 call.getFieldName(),
306 behaviorEntry 269 behaviorEntry
307 ); 270 );
308 m_fieldReferences.put( calledFieldEntry, reference ); 271 m_fieldReferences.put(calledFieldEntry, reference);
309 } 272 }
310 273
311 @Override 274 @Override
312 public void edit( ConstructorCall call ) 275 public void edit(ConstructorCall call) {
313 { 276 String className = Descriptor.toJvmName(call.getClassName());
314 String className = Descriptor.toJvmName( call.getClassName() );
315 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 277 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
316 new ClassEntry( className ), 278 new ClassEntry(className),
317 call.getSignature() 279 call.getSignature()
318 ); 280 );
319 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 281 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -321,15 +283,14 @@ public class JarIndex
321 call.getMethodName(), 283 call.getMethodName(),
322 behaviorEntry 284 behaviorEntry
323 ); 285 );
324 m_behaviorReferences.put( calledConstructorEntry, reference ); 286 m_behaviorReferences.put(calledConstructorEntry, reference);
325 } 287 }
326 288
327 @Override 289 @Override
328 public void edit( NewExpr call ) 290 public void edit(NewExpr call) {
329 { 291 String className = Descriptor.toJvmName(call.getClassName());
330 String className = Descriptor.toJvmName( call.getClassName() );
331 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 292 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
332 new ClassEntry( className ), 293 new ClassEntry(className),
333 call.getSignature() 294 call.getSignature()
334 ); 295 );
335 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 296 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -337,173 +298,141 @@ public class JarIndex
337 call.getClassName(), 298 call.getClassName(),
338 behaviorEntry 299 behaviorEntry
339 ); 300 );
340 m_behaviorReferences.put( calledConstructorEntry, reference ); 301 m_behaviorReferences.put(calledConstructorEntry, reference);
341 } 302 }
342 } ); 303 });
343 } 304 } catch (CannotCompileException ex) {
344 catch( CannotCompileException ex ) 305 throw new Error(ex);
345 {
346 throw new Error( ex );
347 } 306 }
348 } 307 }
349 308
350 public ClassEntry resolveEntryClass( Entry obfEntry ) 309 public ClassEntry resolveEntryClass(Entry obfEntry) {
351 { 310
352 // this entry could refer to a method on a class where the method is not actually implemented 311 // this entry could refer to a method on a class where the method is not actually implemented
353 // travel up the inheritance tree to find the closest implementation 312 // travel up the inheritance tree to find the closest implementation
354 while( !containsObfEntry( obfEntry ) ) 313 while (!containsObfEntry(obfEntry)) {
355 {
356 // is there a parent class? 314 // is there a parent class?
357 String superclassName = m_translationIndex.getSuperclassName( obfEntry.getClassName() ); 315 String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName());
358 if( superclassName == null ) 316 if (superclassName == null) {
359 {
360 // this is probably a method from a class in a library 317 // this is probably a method from a class in a library
361 // we can't trace the implementation up any higher unless we index the library 318 // we can't trace the implementation up any higher unless we index the library
362 return null; 319 return null;
363 } 320 }
364 321
365 // move up to the parent class 322 // move up to the parent class
366 obfEntry = obfEntry.cloneToNewClass( new ClassEntry( superclassName ) ); 323 obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName));
367 } 324 }
368 return obfEntry.getClassEntry(); 325 return obfEntry.getClassEntry();
369 } 326 }
370 327
371 private CtMethod getBridgedMethod( CtMethod method ) 328 private CtMethod getBridgedMethod(CtMethod method) {
372 { 329
373 // bridge methods just call another method, cast it to the return type, and return the result 330 // bridge methods just call another method, cast it to the return type, and return the result
374 // let's see if we can detect this scenario 331 // let's see if we can detect this scenario
375 332
376 // skip non-synthetic methods 333 // skip non-synthetic methods
377 if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 ) 334 if ( (method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
378 {
379 return null; 335 return null;
380 } 336 }
381 337
382 // get all the called methods 338 // get all the called methods
383 final List<MethodCall> methodCalls = Lists.newArrayList(); 339 final List<MethodCall> methodCalls = Lists.newArrayList();
384 try 340 try {
385 { 341 method.instrument(new ExprEditor() {
386 method.instrument( new ExprEditor( )
387 {
388 @Override 342 @Override
389 public void edit( MethodCall call ) 343 public void edit(MethodCall call) {
390 { 344 methodCalls.add(call);
391 methodCalls.add( call );
392 } 345 }
393 } ); 346 });
394 } 347 } catch (CannotCompileException ex) {
395 catch( CannotCompileException ex )
396 {
397 // this is stupid... we're not even compiling anything 348 // this is stupid... we're not even compiling anything
398 throw new Error( ex ); 349 throw new Error(ex);
399 } 350 }
400 351
401 // is there just one? 352 // is there just one?
402 if( methodCalls.size() != 1 ) 353 if (methodCalls.size() != 1) {
403 {
404 return null; 354 return null;
405 } 355 }
406 MethodCall call = methodCalls.get( 0 ); 356 MethodCall call = methodCalls.get(0);
407 357
408 try 358 try {
409 {
410 // we have a bridge method! 359 // we have a bridge method!
411 return call.getMethod(); 360 return call.getMethod();
412 } 361 } catch (NotFoundException ex) {
413 catch( NotFoundException ex )
414 {
415 // can't find the type? not a bridge method 362 // can't find the type? not a bridge method
416 return null; 363 return null;
417 } 364 }
418 } 365 }
419 366
420 private String findOuterClass( CtClass c ) 367 private String findOuterClass(CtClass c) {
421 { 368
422 // inner classes: 369 // inner classes:
423 // have constructors that can (illegally) set synthetic fields 370 // have constructors that can (illegally) set synthetic fields
424 // the outer class is the only class that calls constructors 371 // the outer class is the only class that calls constructors
425 372
426 // use the synthetic fields to find the synthetic constructors 373 // use the synthetic fields to find the synthetic constructors
427 for( CtConstructor constructor : c.getDeclaredConstructors() ) 374 for (CtConstructor constructor : c.getDeclaredConstructors()) {
428 {
429 Set<String> syntheticFieldTypes = Sets.newHashSet(); 375 Set<String> syntheticFieldTypes = Sets.newHashSet();
430 if( !isIllegalConstructor( syntheticFieldTypes, constructor ) ) 376 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
431 {
432 continue; 377 continue;
433 } 378 }
434 379
435 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 380 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
436 ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); 381 ConstructorEntry constructorEntry = new ConstructorEntry(
382 classEntry,
383 constructor.getMethodInfo().getDescriptor()
384 );
437 385
438 // gather the classes from the illegally-set synthetic fields 386 // gather the classes from the illegally-set synthetic fields
439 Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); 387 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
440 for( String type : syntheticFieldTypes ) 388 for (String type : syntheticFieldTypes) {
441 { 389 if (type.startsWith("L")) {
442 if( type.startsWith( "L" ) ) 390 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
443 { 391 if (isSaneOuterClass(outerClassEntry, classEntry)) {
444 ClassEntry outerClassEntry = new ClassEntry( type.substring( 1, type.length() - 1 ) ); 392 illegallySetClasses.add(outerClassEntry);
445 if( isSaneOuterClass( outerClassEntry, classEntry ) )
446 {
447 illegallySetClasses.add( outerClassEntry );
448 } 393 }
449 } 394 }
450 } 395 }
451 396
452 // who calls this constructor? 397 // who calls this constructor?
453 Set<ClassEntry> callerClasses = Sets.newHashSet(); 398 Set<ClassEntry> callerClasses = Sets.newHashSet();
454 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences( constructorEntry ) ) 399 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
455 { 400
456 // make sure it's not a call to super 401 // make sure it's not a call to super
457 if( reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry ) 402 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
458 { 403
459 // is the entry a superclass of the context? 404 // is the entry a superclass of the context?
460 String calledClassName = reference.entry.getClassName(); 405 String calledClassName = reference.entry.getClassName();
461 String callerSuperclassName = m_translationIndex.getSuperclassName( reference.context.getClassName() ); 406 String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName());
462 if( callerSuperclassName != null && callerSuperclassName.equals( calledClassName ) ) 407 if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) {
463 {
464 // it's a super call, skip 408 // it's a super call, skip
465 continue; 409 continue;
466 } 410 }
467 } 411 }
468 412
469 if( isSaneOuterClass( reference.context.getClassEntry(), classEntry ) ) 413 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
470 { 414 callerClasses.add(reference.context.getClassEntry());
471 callerClasses.add( reference.context.getClassEntry() );
472 } 415 }
473 } 416 }
474 417
475 // do we have an answer yet? 418 // do we have an answer yet?
476 if( callerClasses.isEmpty() ) 419 if (callerClasses.isEmpty()) {
477 { 420 if (illegallySetClasses.size() == 1) {
478 if( illegallySetClasses.size() == 1 )
479 {
480 return illegallySetClasses.iterator().next().getName(); 421 return illegallySetClasses.iterator().next().getName();
422 } else {
423 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
481 } 424 }
482 else 425 } else {
483 { 426 if (callerClasses.size() == 1) {
484 System.out.println( String.format( "WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry ) );
485 }
486 }
487 else
488 {
489 if( callerClasses.size() == 1 )
490 {
491 return callerClasses.iterator().next().getName(); 427 return callerClasses.iterator().next().getName();
492 } 428 } else {
493 else
494 {
495 // multiple callers, do the illegally set classes narrow it down? 429 // multiple callers, do the illegally set classes narrow it down?
496 Set<ClassEntry> intersection = Sets.newHashSet( callerClasses ); 430 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
497 intersection.retainAll( illegallySetClasses ); 431 intersection.retainAll(illegallySetClasses);
498 if( intersection.size() == 1 ) 432 if (intersection.size() == 1) {
499 {
500 return intersection.iterator().next().getName(); 433 return intersection.iterator().next().getName();
501 } 434 } else {
502 else 435 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
503 {
504 System.out.println( String.format( "WARNING: Unable to choose outer class for %s among options: %s",
505 classEntry, callerClasses
506 ) );
507 } 436 }
508 } 437 }
509 } 438 }
@@ -512,99 +441,82 @@ public class JarIndex
512 return null; 441 return null;
513 } 442 }
514 443
515 private boolean isSaneOuterClass( ClassEntry outerClassEntry, ClassEntry innerClassEntry ) 444 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) {
516 { 445
517 // clearly this would be silly 446 // clearly this would be silly
518 if( outerClassEntry.equals( innerClassEntry ) ) 447 if (outerClassEntry.equals(innerClassEntry)) {
519 {
520 return false; 448 return false;
521 } 449 }
522 450
523 // is the outer class in the jar? 451 // is the outer class in the jar?
524 if( !m_obfClassEntries.contains( outerClassEntry ) ) 452 if (!m_obfClassEntries.contains(outerClassEntry)) {
525 {
526 return false; 453 return false;
527 } 454 }
528 455
529 return true; 456 return true;
530 } 457 }
531 458
532 @SuppressWarnings( "unchecked" ) 459 @SuppressWarnings("unchecked")
533 private boolean isIllegalConstructor( Set<String> syntheticFieldTypes, CtConstructor constructor ) 460 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) {
534 { 461
535 // illegal constructors only set synthetic member fields, then call super() 462 // illegal constructors only set synthetic member fields, then call super()
536 String className = constructor.getDeclaringClass().getName(); 463 String className = constructor.getDeclaringClass().getName();
537 464
538 // collect all the field accesses, constructor calls, and method calls 465 // collect all the field accesses, constructor calls, and method calls
539 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); 466 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
540 final List<ConstructorCall> constructorCalls = Lists.newArrayList(); 467 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
541 try 468 try {
542 { 469 constructor.instrument(new ExprEditor() {
543 constructor.instrument( new ExprEditor( )
544 {
545 @Override 470 @Override
546 public void edit( FieldAccess fieldAccess ) 471 public void edit(FieldAccess fieldAccess) {
547 { 472 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
548 if( fieldAccess.isWriter() && constructorCalls.isEmpty() ) 473 illegalFieldWrites.add(fieldAccess);
549 {
550 illegalFieldWrites.add( fieldAccess );
551 } 474 }
552 } 475 }
553 476
554 @Override 477 @Override
555 public void edit( ConstructorCall constructorCall ) 478 public void edit(ConstructorCall constructorCall) {
556 { 479 constructorCalls.add(constructorCall);
557 constructorCalls.add( constructorCall );
558 } 480 }
559 } ); 481 });
560 } 482 } catch (CannotCompileException ex) {
561 catch( CannotCompileException ex )
562 {
563 // we're not compiling anything... this is stupid 483 // we're not compiling anything... this is stupid
564 throw new Error( ex ); 484 throw new Error(ex);
565 } 485 }
566 486
567 // are there any illegal field writes? 487 // are there any illegal field writes?
568 if( illegalFieldWrites.isEmpty() ) 488 if (illegalFieldWrites.isEmpty()) {
569 {
570 return false; 489 return false;
571 } 490 }
572 491
573 // are all the writes to synthetic fields? 492 // are all the writes to synthetic fields?
574 for( FieldAccess fieldWrite : illegalFieldWrites ) 493 for (FieldAccess fieldWrite : illegalFieldWrites) {
575 { 494
576 // all illegal writes have to be to the local class 495 // all illegal writes have to be to the local class
577 if( !fieldWrite.getClassName().equals( className ) ) 496 if (!fieldWrite.getClassName().equals(className)) {
578 { 497 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
579 System.err.println( String.format( "WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName() ) );
580 return false; 498 return false;
581 } 499 }
582 500
583 // find the field 501 // find the field
584 FieldInfo fieldInfo = null; 502 FieldInfo fieldInfo = null;
585 for( FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields() ) 503 for (FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields()) {
586 { 504 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
587 if( info.getName().equals( fieldWrite.getFieldName() ) && info.getDescriptor().equals( fieldWrite.getSignature() ) )
588 {
589 fieldInfo = info; 505 fieldInfo = info;
590 break; 506 break;
591 } 507 }
592 } 508 }
593 if( fieldInfo == null ) 509 if (fieldInfo == null) {
594 {
595 // field is in a superclass or something, can't be a local synthetic member 510 // field is in a superclass or something, can't be a local synthetic member
596 return false; 511 return false;
597 } 512 }
598 513
599 // is this field synthetic? 514 // is this field synthetic?
600 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 515 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
601 if( isSynthetic ) 516 if (isSynthetic) {
602 { 517 syntheticFieldTypes.add(fieldInfo.getDescriptor());
603 syntheticFieldTypes.add( fieldInfo.getDescriptor() ); 518 } else {
604 } 519 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
605 else
606 {
607 System.err.println( String.format( "WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName() ) );
608 return false; 520 return false;
609 } 521 }
610 } 522 }
@@ -612,56 +524,51 @@ public class JarIndex
612 // we passed all the tests! 524 // we passed all the tests!
613 return true; 525 return true;
614 } 526 }
615 527
616 private BehaviorEntry isAnonymousClass( CtClass c, String outerClassName ) 528 private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) {
617 { 529
618 ClassEntry innerClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 530 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
619 531
620 // anonymous classes: 532 // anonymous classes:
621 // can't be abstract 533 // can't be abstract
622 // have only one constructor 534 // have only one constructor
623 // it's called exactly once by the outer class 535 // it's called exactly once by the outer class
624 // the type the instance is assigned to can't be this type 536 // the type the instance is assigned to can't be this type
625 537
626 // is abstract? 538 // is abstract?
627 if( Modifier.isAbstract( c.getModifiers() ) ) 539 if (Modifier.isAbstract(c.getModifiers())) {
628 {
629 return null; 540 return null;
630 } 541 }
631 542
632 // is there exactly one constructor? 543 // is there exactly one constructor?
633 if( c.getDeclaredConstructors().length != 1 ) 544 if (c.getDeclaredConstructors().length != 1) {
634 {
635 return null; 545 return null;
636 } 546 }
637 CtConstructor constructor = c.getDeclaredConstructors()[0]; 547 CtConstructor constructor = c.getDeclaredConstructors()[0];
638 548
639 // is this constructor called exactly once? 549 // is this constructor called exactly once?
640 ConstructorEntry constructorEntry = new ConstructorEntry( innerClassEntry, constructor.getMethodInfo().getDescriptor() ); 550 ConstructorEntry constructorEntry = new ConstructorEntry(
641 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = getBehaviorReferences( constructorEntry ); 551 innerClassEntry,
642 if( references.size() != 1 ) 552 constructor.getMethodInfo().getDescriptor()
643 { 553 );
554 Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
555 if (references.size() != 1) {
644 return null; 556 return null;
645 } 557 }
646 558
647 // does the caller use this type? 559 // does the caller use this type?
648 BehaviorEntry caller = references.iterator().next().context; 560 BehaviorEntry caller = references.iterator().next().context;
649 for( FieldEntry fieldEntry : getReferencedFields( caller ) ) 561 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
650 { 562 ClassEntry fieldClass = getFieldClass(fieldEntry);
651 ClassEntry fieldClass = getFieldClass( fieldEntry ); 563 if (fieldClass != null && fieldClass.equals(innerClassEntry)) {
652 if( fieldClass != null && fieldClass.equals( innerClassEntry ) )
653 {
654 // caller references this type, so it can't be anonymous 564 // caller references this type, so it can't be anonymous
655 return null; 565 return null;
656 } 566 }
657 } 567 }
658 for( BehaviorEntry behaviorEntry : getReferencedBehaviors( caller ) ) 568 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
659 {
660 // get the class types from the signature 569 // get the class types from the signature
661 for( String className : SignatureUpdater.getClasses( behaviorEntry.getSignature() ) ) 570 for (String className : SignatureUpdater.getClasses(behaviorEntry.getSignature())) {
662 { 571 if (className.equals(innerClassEntry.getName())) {
663 if( className.equals( innerClassEntry.getName() ) )
664 {
665 // caller references this type, so it can't be anonymous 572 // caller references this type, so it can't be anonymous
666 return null; 573 return null;
667 } 574 }
@@ -670,330 +577,275 @@ public class JarIndex
670 577
671 return caller; 578 return caller;
672 } 579 }
673 580
674 public Set<ClassEntry> getObfClassEntries( ) 581 public Set<ClassEntry> getObfClassEntries() {
675 {
676 return m_obfClassEntries; 582 return m_obfClassEntries;
677 } 583 }
678 584
679 public TranslationIndex getTranslationIndex( ) 585 public TranslationIndex getTranslationIndex() {
680 {
681 return m_translationIndex; 586 return m_translationIndex;
682 } 587 }
683 588
684 public Access getAccess( Entry entry ) 589 public Access getAccess(Entry entry) {
685 { 590 return m_access.get(entry);
686 return m_access.get( entry );
687 } 591 }
688 592
689 public ClassEntry getFieldClass( FieldEntry fieldEntry ) 593 public ClassEntry getFieldClass(FieldEntry fieldEntry) {
690 { 594 return m_fieldClasses.get(fieldEntry);
691 return m_fieldClasses.get( fieldEntry );
692 } 595 }
693 596
694 public ClassInheritanceTreeNode getClassInheritance( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) 597 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
695 { 598
696 // get the root node 599 // get the root node
697 List<String> ancestry = Lists.newArrayList(); 600 List<String> ancestry = Lists.newArrayList();
698 ancestry.add( obfClassEntry.getName() ); 601 ancestry.add(obfClassEntry.getName());
699 ancestry.addAll( m_translationIndex.getAncestry( obfClassEntry.getName() ) ); 602 ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName()));
700 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); 603 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
604 deobfuscatingTranslator,
605 ancestry.get(ancestry.size() - 1)
606 );
701 607
702 // expand all children recursively 608 // expand all children recursively
703 rootNode.load( m_translationIndex, true ); 609 rootNode.load(m_translationIndex, true);
704 610
705 return rootNode; 611 return rootNode;
706 } 612 }
707 613
708 public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) 614 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
709 { 615
710 // is this even an interface? 616 // is this even an interface?
711 if( isInterface( obfClassEntry.getClassName() ) ) 617 if (isInterface(obfClassEntry.getClassName())) {
712 { 618 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
713 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); 619 node.load(this);
714 node.load( this );
715 return node; 620 return node;
716 } 621 }
717 return null; 622 return null;
718 } 623 }
719 624
720 public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) 625 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
721 { 626
722 // travel to the ancestor implementation 627 // travel to the ancestor implementation
723 String baseImplementationClassName = obfMethodEntry.getClassName(); 628 String baseImplementationClassName = obfMethodEntry.getClassName();
724 for( String ancestorClassName : m_translationIndex.getAncestry( obfMethodEntry.getClassName() ) ) 629 for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) {
725 {
726 MethodEntry ancestorMethodEntry = new MethodEntry( 630 MethodEntry ancestorMethodEntry = new MethodEntry(
727 new ClassEntry( ancestorClassName ), 631 new ClassEntry(ancestorClassName),
728 obfMethodEntry.getName(), 632 obfMethodEntry.getName(),
729 obfMethodEntry.getSignature() 633 obfMethodEntry.getSignature()
730 ); 634 );
731 if( containsObfBehavior( ancestorMethodEntry ) ) 635 if (containsObfBehavior(ancestorMethodEntry)) {
732 {
733 baseImplementationClassName = ancestorClassName; 636 baseImplementationClassName = ancestorClassName;
734 } 637 }
735 } 638 }
736 639
737 // make a root node at the base 640 // make a root node at the base
738 MethodEntry methodEntry = new MethodEntry( 641 MethodEntry methodEntry = new MethodEntry(
739 new ClassEntry( baseImplementationClassName ), 642 new ClassEntry(baseImplementationClassName),
740 obfMethodEntry.getName(), 643 obfMethodEntry.getName(),
741 obfMethodEntry.getSignature() 644 obfMethodEntry.getSignature()
742 ); 645 );
743 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 646 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
744 deobfuscatingTranslator, 647 deobfuscatingTranslator,
745 methodEntry, 648 methodEntry,
746 containsObfBehavior( methodEntry ) 649 containsObfBehavior(methodEntry)
747 ); 650 );
748 651
749 // expand the full tree 652 // expand the full tree
750 rootNode.load( this, true ); 653 rootNode.load(this, true);
751 654
752 return rootNode; 655 return rootNode;
753 } 656 }
754 657
755 public MethodImplementationsTreeNode getMethodImplementations( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) 658 public MethodImplementationsTreeNode getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
756 { 659
757 MethodEntry interfaceMethodEntry; 660 MethodEntry interfaceMethodEntry;
758 661
759 // is this method on an interface? 662 // is this method on an interface?
760 if( isInterface( obfMethodEntry.getClassName() ) ) 663 if (isInterface(obfMethodEntry.getClassName())) {
761 {
762 interfaceMethodEntry = obfMethodEntry; 664 interfaceMethodEntry = obfMethodEntry;
763 } 665 } else {
764 else
765 {
766 // get the interface class 666 // get the interface class
767 List<MethodEntry> methodInterfaces = Lists.newArrayList(); 667 List<MethodEntry> methodInterfaces = Lists.newArrayList();
768 for( String interfaceName : getInterfaces( obfMethodEntry.getClassName() ) ) 668 for (String interfaceName : getInterfaces(obfMethodEntry.getClassName())) {
769 {
770 // is this method defined in this interface? 669 // is this method defined in this interface?
771 MethodEntry methodInterface = new MethodEntry( 670 MethodEntry methodInterface = new MethodEntry(
772 new ClassEntry( interfaceName ), 671 new ClassEntry(interfaceName),
773 obfMethodEntry.getName(), 672 obfMethodEntry.getName(),
774 obfMethodEntry.getSignature() 673 obfMethodEntry.getSignature()
775 ); 674 );
776 if( containsObfBehavior( methodInterface ) ) 675 if (containsObfBehavior(methodInterface)) {
777 { 676 methodInterfaces.add(methodInterface);
778 methodInterfaces.add( methodInterface );
779 } 677 }
780 } 678 }
781 if( methodInterfaces.isEmpty() ) 679 if (methodInterfaces.isEmpty()) {
782 {
783 return null; 680 return null;
784 } 681 }
785 if( methodInterfaces.size() > 1 ) 682 if (methodInterfaces.size() > 1) {
786 { 683 throw new Error("Too many interfaces define this method! This is not yet supported by Enigma!");
787 throw new Error( "Too many interfaces define this method! This is not yet supported by Enigma!" );
788 } 684 }
789 interfaceMethodEntry = methodInterfaces.get( 0 ); 685 interfaceMethodEntry = methodInterfaces.get(0);
790 } 686 }
791 687
792 MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode( deobfuscatingTranslator, interfaceMethodEntry ); 688 MethodImplementationsTreeNode rootNode = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
793 rootNode.load( this ); 689 rootNode.load(this);
794 return rootNode; 690 return rootNode;
795 } 691 }
796 692
797 public Set<MethodEntry> getRelatedMethodImplementations( MethodEntry obfMethodEntry ) 693 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
798 {
799 Set<MethodEntry> methodEntries = Sets.newHashSet(); 694 Set<MethodEntry> methodEntries = Sets.newHashSet();
800 getRelatedMethodImplementations( methodEntries, getMethodInheritance( null, obfMethodEntry ) ); 695 getRelatedMethodImplementations(methodEntries, getMethodInheritance(null, obfMethodEntry));
801 return methodEntries; 696 return methodEntries;
802 } 697 }
803 698
804 private void getRelatedMethodImplementations( Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node ) 699 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
805 {
806 MethodEntry methodEntry = node.getMethodEntry(); 700 MethodEntry methodEntry = node.getMethodEntry();
807 if( containsObfBehavior( methodEntry ) ) 701 if (containsObfBehavior(methodEntry)) {
808 {
809 // collect the entry 702 // collect the entry
810 methodEntries.add( methodEntry ); 703 methodEntries.add(methodEntry);
811 } 704 }
812 705
813 // look at interface methods too 706 // look at interface methods too
814 MethodImplementationsTreeNode implementations = getMethodImplementations( null, methodEntry ); 707 MethodImplementationsTreeNode implementations = getMethodImplementations(null, methodEntry);
815 if( implementations != null ) 708 if (implementations != null) {
816 { 709 getRelatedMethodImplementations(methodEntries, implementations);
817 getRelatedMethodImplementations( methodEntries, implementations );
818 } 710 }
819 711
820 // recurse 712 // recurse
821 for( int i=0; i<node.getChildCount(); i++ ) 713 for (int i = 0; i < node.getChildCount(); i++) {
822 { 714 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode)node.getChildAt(i));
823 getRelatedMethodImplementations( methodEntries, (MethodInheritanceTreeNode)node.getChildAt( i ) );
824 } 715 }
825 } 716 }
826 717
827 private void getRelatedMethodImplementations( Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node ) 718 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
828 {
829 MethodEntry methodEntry = node.getMethodEntry(); 719 MethodEntry methodEntry = node.getMethodEntry();
830 if( containsObfBehavior( methodEntry ) ) 720 if (containsObfBehavior(methodEntry)) {
831 {
832 // collect the entry 721 // collect the entry
833 methodEntries.add( methodEntry ); 722 methodEntries.add(methodEntry);
834 } 723 }
835 724
836 // recurse 725 // recurse
837 for( int i=0; i<node.getChildCount(); i++ ) 726 for (int i = 0; i < node.getChildCount(); i++) {
838 { 727 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode)node.getChildAt(i));
839 getRelatedMethodImplementations( methodEntries, (MethodImplementationsTreeNode)node.getChildAt( i ) );
840 } 728 }
841 } 729 }
842 730
843 public Collection<EntryReference<FieldEntry,BehaviorEntry>> getFieldReferences( FieldEntry fieldEntry ) 731 public Collection<EntryReference<FieldEntry,BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) {
844 { 732 return m_fieldReferences.get(fieldEntry);
845 return m_fieldReferences.get( fieldEntry );
846 } 733 }
847 734
848 public Collection<FieldEntry> getReferencedFields( BehaviorEntry behaviorEntry ) 735 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) {
849 {
850 // linear search is fast enough for now 736 // linear search is fast enough for now
851 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 737 Set<FieldEntry> fieldEntries = Sets.newHashSet();
852 for( EntryReference<FieldEntry,BehaviorEntry> reference : m_fieldReferences.values() ) 738 for (EntryReference<FieldEntry,BehaviorEntry> reference : m_fieldReferences.values()) {
853 { 739 if (reference.context == behaviorEntry) {
854 if( reference.context == behaviorEntry ) 740 fieldEntries.add(reference.entry);
855 {
856 fieldEntries.add( reference.entry );
857 } 741 }
858 } 742 }
859 return fieldEntries; 743 return fieldEntries;
860 } 744 }
861 745
862 public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences( BehaviorEntry behaviorEntry ) 746 public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) {
863 { 747 return m_behaviorReferences.get(behaviorEntry);
864 return m_behaviorReferences.get( behaviorEntry );
865 } 748 }
866 749
867 public Collection<BehaviorEntry> getReferencedBehaviors( BehaviorEntry behaviorEntry ) 750 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) {
868 {
869 // linear search is fast enough for now 751 // linear search is fast enough for now
870 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); 752 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
871 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : m_behaviorReferences.values() ) 753 for (EntryReference<BehaviorEntry,BehaviorEntry> reference : m_behaviorReferences.values()) {
872 { 754 if (reference.context == behaviorEntry) {
873 if( reference.context == behaviorEntry ) 755 behaviorEntries.add(reference.entry);
874 {
875 behaviorEntries.add( reference.entry );
876 } 756 }
877 } 757 }
878 return behaviorEntries; 758 return behaviorEntries;
879 } 759 }
880 760
881 public Collection<String> getInnerClasses( String obfOuterClassName ) 761 public Collection<String> getInnerClasses(String obfOuterClassName) {
882 { 762 return m_innerClasses.get(obfOuterClassName);
883 return m_innerClasses.get( obfOuterClassName );
884 } 763 }
885 764
886 public String getOuterClass( String obfInnerClassName ) 765 public String getOuterClass(String obfInnerClassName) {
887 {
888 // make sure we use the right name 766 // make sure we use the right name
889 if( new ClassEntry( obfInnerClassName ).getPackageName() != null ) 767 if (new ClassEntry(obfInnerClassName).getPackageName() != null) {
890 { 768 throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName);
891 throw new IllegalArgumentException( "Don't reference obfuscated inner classes using packages: " + obfInnerClassName );
892 } 769 }
893 return m_outerClasses.get( obfInnerClassName ); 770 return m_outerClasses.get(obfInnerClassName);
894 } 771 }
895 772
896 public boolean isAnonymousClass( String obfInnerClassName ) 773 public boolean isAnonymousClass(String obfInnerClassName) {
897 { 774 return m_anonymousClasses.containsKey(obfInnerClassName);
898 return m_anonymousClasses.containsKey( obfInnerClassName );
899 } 775 }
900 776
901 public BehaviorEntry getAnonymousClassCaller( String obfInnerClassName ) 777 public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) {
902 { 778 return m_anonymousClasses.get(obfInnerClassName);
903 return m_anonymousClasses.get( obfInnerClassName );
904 } 779 }
905 780
906 public Set<String> getInterfaces( String className ) 781 public Set<String> getInterfaces(String className) {
907 {
908 Set<String> interfaceNames = new HashSet<String>(); 782 Set<String> interfaceNames = new HashSet<String>();
909 interfaceNames.addAll( m_interfaces.get( className ) ); 783 interfaceNames.addAll(m_interfaces.get(className));
910 for( String ancestor : m_translationIndex.getAncestry( className ) ) 784 for (String ancestor : m_translationIndex.getAncestry(className)) {
911 { 785 interfaceNames.addAll(m_interfaces.get(ancestor));
912 interfaceNames.addAll( m_interfaces.get( ancestor ) );
913 } 786 }
914 return interfaceNames; 787 return interfaceNames;
915 } 788 }
916 789
917 public Set<String> getImplementingClasses( String targetInterfaceName ) 790 public Set<String> getImplementingClasses(String targetInterfaceName) {
918 {
919 // linear search is fast enough for now 791 // linear search is fast enough for now
920 Set<String> classNames = Sets.newHashSet(); 792 Set<String> classNames = Sets.newHashSet();
921 for( Map.Entry<String,String> entry : m_interfaces.entries() ) 793 for (Map.Entry<String,String> entry : m_interfaces.entries()) {
922 {
923 String className = entry.getKey(); 794 String className = entry.getKey();
924 String interfaceName = entry.getValue(); 795 String interfaceName = entry.getValue();
925 if( interfaceName.equals( targetInterfaceName ) ) 796 if (interfaceName.equals(targetInterfaceName)) {
926 { 797 classNames.add(className);
927 classNames.add( className ); 798 m_translationIndex.getSubclassNamesRecursively(classNames, className);
928 m_translationIndex.getSubclassNamesRecursively( classNames, className );
929 } 799 }
930 } 800 }
931 return classNames; 801 return classNames;
932 } 802 }
933 803
934 public boolean isInterface( String className ) 804 public boolean isInterface(String className) {
935 { 805 return m_interfaces.containsValue(className);
936 return m_interfaces.containsValue( className );
937 } 806 }
938 807
939 public MethodEntry getBridgeMethod( MethodEntry methodEntry ) 808 public MethodEntry getBridgeMethod(MethodEntry methodEntry) {
940 { 809 return m_bridgeMethods.get(methodEntry);
941 return m_bridgeMethods.get( methodEntry );
942 } 810 }
943 811
944 public boolean containsObfClass( ClassEntry obfClassEntry ) 812 public boolean containsObfClass(ClassEntry obfClassEntry) {
945 { 813 return m_obfClassEntries.contains(obfClassEntry);
946 return m_obfClassEntries.contains( obfClassEntry );
947 } 814 }
948 815
949 public boolean containsObfField( FieldEntry obfFieldEntry ) 816 public boolean containsObfField(FieldEntry obfFieldEntry) {
950 { 817 return m_access.containsKey(obfFieldEntry);
951 return m_access.containsKey( obfFieldEntry );
952 } 818 }
953 819
954 public boolean containsObfBehavior( BehaviorEntry obfBehaviorEntry ) 820 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) {
955 { 821 return m_access.containsKey(obfBehaviorEntry);
956 return m_access.containsKey( obfBehaviorEntry );
957 } 822 }
958 823
959 public boolean containsObfArgument( ArgumentEntry obfArgumentEntry ) 824 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
960 {
961 // check the behavior 825 // check the behavior
962 if( !containsObfBehavior( obfArgumentEntry.getBehaviorEntry() ) ) 826 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
963 {
964 return false; 827 return false;
965 } 828 }
966 829
967 // check the argument 830 // check the argument
968 if( obfArgumentEntry.getIndex() >= Descriptor.numOfParameters( obfArgumentEntry.getBehaviorEntry().getSignature() ) ) 831 if (obfArgumentEntry.getIndex() >= Descriptor.numOfParameters(obfArgumentEntry.getBehaviorEntry().getSignature())) {
969 {
970 return false; 832 return false;
971 } 833 }
972 834
973 return true; 835 return true;
974 } 836 }
975 837
976 public boolean containsObfEntry( Entry obfEntry ) 838 public boolean containsObfEntry(Entry obfEntry) {
977 { 839 if (obfEntry instanceof ClassEntry) {
978 if( obfEntry instanceof ClassEntry ) 840 return containsObfClass((ClassEntry)obfEntry);
979 { 841 } else if (obfEntry instanceof FieldEntry) {
980 return containsObfClass( (ClassEntry)obfEntry ); 842 return containsObfField((FieldEntry)obfEntry);
981 } 843 } else if (obfEntry instanceof BehaviorEntry) {
982 else if( obfEntry instanceof FieldEntry ) 844 return containsObfBehavior((BehaviorEntry)obfEntry);
983 { 845 } else if (obfEntry instanceof ArgumentEntry) {
984 return containsObfField( (FieldEntry)obfEntry ); 846 return containsObfArgument((ArgumentEntry)obfEntry);
985 } 847 } else {
986 else if( obfEntry instanceof BehaviorEntry ) 848 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
987 {
988 return containsObfBehavior( (BehaviorEntry)obfEntry );
989 }
990 else if( obfEntry instanceof ArgumentEntry )
991 {
992 return containsObfArgument( (ArgumentEntry)obfEntry );
993 }
994 else
995 {
996 throw new Error( "Entry type not supported: " + obfEntry.getClass().getName() );
997 } 849 }
998 } 850 }
999} 851}