diff options
| author | 2014-09-21 00:32:03 -0400 | |
|---|---|---|
| committer | 2014-09-21 00:32:03 -0400 | |
| commit | 8409dea980fa03c06b180969c5e0696f7cb5474b (patch) | |
| tree | eb0cdfad4bc2cee6df8a311e62fc2f60d0360970 | |
| parent | removed workaround for procyon bug (diff) | |
| download | enigma-fork-8409dea980fa03c06b180969c5e0696f7cb5474b.tar.gz enigma-fork-8409dea980fa03c06b180969c5e0696f7cb5474b.tar.xz enigma-fork-8409dea980fa03c06b180969c5e0696f7cb5474b.zip | |
started unit testing for inner/anonymous class detection
| -rw-r--r-- | build.gradle | 47 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 181 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/SignatureUpdater.java | 18 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestDeobfuscator.java | 2 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestInnerClasses.java | 64 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestJarIndexConstructorReferences.java | 10 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestJarIndexInheritanceTree.java | 14 | ||||
| -rw-r--r-- | test/cuchaz/enigma/TestJarIndexLoneClass.java | 14 | ||||
| -rw-r--r-- | test/cuchaz/enigma/inputs/innerClasses/Anonymous.java | 17 | ||||
| -rw-r--r-- | test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java | 22 | ||||
| -rw-r--r-- | test/cuchaz/enigma/inputs/innerClasses/Simple.java | 9 |
11 files changed, 306 insertions, 92 deletions
diff --git a/build.gradle b/build.gradle index 767b032..6b89d32 100644 --- a/build.gradle +++ b/build.gradle | |||
| @@ -90,21 +90,46 @@ task jarConstructors( type: Jar ) { | |||
| 90 | archiveName( "testConstructors.jar" ) | 90 | archiveName( "testConstructors.jar" ) |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | task obfTestCases( type: proguard.gradle.ProGuardTask ) { | 93 | task jarInnerClasses( type: Jar ) { |
| 94 | dependsOn jarLoneClass, jarInheritanceTree, jarConstructors | 94 | from( sourceSets.test.output ) { |
| 95 | 95 | include( "cuchaz/enigma/inputs/Keep.class" ) | |
| 96 | include( "cuchaz/enigma/inputs/innerClasses/**" ) | ||
| 97 | } | ||
| 98 | archiveName( "testInnerClasses.jar" ) | ||
| 99 | } | ||
| 100 | |||
| 101 | tasks.withType( proguard.gradle.ProGuardTask ) { | ||
| 96 | libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) | 102 | libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) |
| 97 | overloadaggressively | 103 | overloadaggressively |
| 98 | repackageclasses | 104 | repackageclasses |
| 99 | allowaccessmodification | 105 | allowaccessmodification |
| 100 | dontoptimize | 106 | dontoptimize |
| 101 | dontshrink | 107 | dontshrink |
| 102 | |||
| 103 | keep( "class cuchaz.enigma.inputs.Keep" ) | 108 | keep( "class cuchaz.enigma.inputs.Keep" ) |
| 104 | 109 | } | |
| 105 | def jarNames = [ "LoneClass", "InheritanceTree", "Constructors" ]; | 110 | |
| 106 | jarNames.each() { | 111 | task obfLoneClass( type: proguard.gradle.ProGuardTask, dependsOn: jarLoneClass ) { |
| 107 | injars( "build/libs/test${it}.jar" ) | 112 | def name = "LoneClass" |
| 108 | outjars( "build/libs/test${it}.obf.jar" ) | 113 | injars( "build/libs/test${name}.jar" ) |
| 109 | } | 114 | outjars( "build/libs/test${name}.obf.jar" ) |
| 110 | } \ No newline at end of file | 115 | } |
| 116 | |||
| 117 | task obfInheritanceTree( type: proguard.gradle.ProGuardTask, dependsOn: jarInheritanceTree ) { | ||
| 118 | def name = "InheritanceTree" | ||
| 119 | injars( "build/libs/test${name}.jar" ) | ||
| 120 | outjars( "build/libs/test${name}.obf.jar" ) | ||
| 121 | } | ||
| 122 | |||
| 123 | task obfConstructors( type: proguard.gradle.ProGuardTask, dependsOn: jarConstructors ) { | ||
| 124 | def name = "Constructors" | ||
| 125 | injars( "build/libs/test${name}.jar" ) | ||
| 126 | outjars( "build/libs/test${name}.obf.jar" ) | ||
| 127 | } | ||
| 128 | |||
| 129 | task obfInnerClasses( type: proguard.gradle.ProGuardTask, dependsOn: jarInnerClasses ) { | ||
| 130 | def name = "InnerClasses" | ||
| 131 | injars( "build/libs/test${name}.jar" ) | ||
| 132 | outjars( "build/libs/test${name}.obf.jar" ) | ||
| 133 | } | ||
| 134 | |||
| 135 | task obfTestCases( dependsOn: tasks.withType( proguard.gradle.ProGuardTask ) ) | ||
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6c955bc..43e2bf6 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -48,6 +48,7 @@ import cuchaz.enigma.mapping.ConstructorEntry; | |||
| 48 | import cuchaz.enigma.mapping.Entry; | 48 | import cuchaz.enigma.mapping.Entry; |
| 49 | import cuchaz.enigma.mapping.FieldEntry; | 49 | import cuchaz.enigma.mapping.FieldEntry; |
| 50 | import cuchaz.enigma.mapping.MethodEntry; | 50 | import cuchaz.enigma.mapping.MethodEntry; |
| 51 | import cuchaz.enigma.mapping.SignatureUpdater; | ||
| 51 | import cuchaz.enigma.mapping.Translator; | 52 | import cuchaz.enigma.mapping.Translator; |
| 52 | 53 | ||
| 53 | public class JarIndex | 54 | public class JarIndex |
| @@ -56,6 +57,7 @@ public class JarIndex | |||
| 56 | private TranslationIndex m_translationIndex; | 57 | private TranslationIndex m_translationIndex; |
| 57 | private Multimap<String,String> m_interfaces; | 58 | private Multimap<String,String> m_interfaces; |
| 58 | private Map<Entry,Access> m_access; | 59 | private Map<Entry,Access> m_access; |
| 60 | private Map<FieldEntry,ClassEntry> m_fieldClasses; | ||
| 59 | private Multimap<String,MethodEntry> m_methodImplementations; | 61 | private Multimap<String,MethodEntry> m_methodImplementations; |
| 60 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; | 62 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; |
| 61 | private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences; | 63 | private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences; |
| @@ -70,6 +72,7 @@ public class JarIndex | |||
| 70 | m_translationIndex = new TranslationIndex(); | 72 | m_translationIndex = new TranslationIndex(); |
| 71 | m_interfaces = HashMultimap.create(); | 73 | m_interfaces = HashMultimap.create(); |
| 72 | m_access = Maps.newHashMap(); | 74 | m_access = Maps.newHashMap(); |
| 75 | m_fieldClasses = Maps.newHashMap(); | ||
| 73 | m_methodImplementations = HashMultimap.create(); | 76 | m_methodImplementations = HashMultimap.create(); |
| 74 | m_behaviorReferences = HashMultimap.create(); | 77 | m_behaviorReferences = HashMultimap.create(); |
| 75 | m_fieldReferences = HashMultimap.create(); | 78 | m_fieldReferences = HashMultimap.create(); |
| @@ -127,7 +130,7 @@ public class JarIndex | |||
| 127 | } | 130 | } |
| 128 | for( CtField field : c.getDeclaredFields() ) | 131 | for( CtField field : c.getDeclaredFields() ) |
| 129 | { | 132 | { |
| 130 | m_translationIndex.addField( className, field.getName() ); | 133 | indexField( field ); |
| 131 | } | 134 | } |
| 132 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) | 135 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) |
| 133 | { | 136 | { |
| @@ -193,6 +196,22 @@ public class JarIndex | |||
| 193 | EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); | 196 | EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); |
| 194 | } | 197 | } |
| 195 | 198 | ||
| 199 | private void indexField( CtField field ) | ||
| 200 | { | ||
| 201 | // get the field entry | ||
| 202 | String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); | ||
| 203 | FieldEntry fieldEntry = new FieldEntry( new ClassEntry( className ), field.getName() ); | ||
| 204 | |||
| 205 | m_translationIndex.addField( className, field.getName() ); | ||
| 206 | |||
| 207 | // is the field a class type? | ||
| 208 | if( field.getSignature().startsWith( "L" ) ) | ||
| 209 | { | ||
| 210 | ClassEntry fieldTypeEntry = new ClassEntry( field.getSignature().substring( 1, field.getSignature().length() - 1 ) ); | ||
| 211 | m_fieldClasses.put( fieldEntry, fieldTypeEntry ); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 196 | private void indexBehavior( CtBehavior behavior ) | 215 | private void indexBehavior( CtBehavior behavior ) |
| 197 | { | 216 | { |
| 198 | // get the behavior entry | 217 | // get the behavior entry |
| @@ -412,7 +431,8 @@ public class JarIndex | |||
| 412 | // use the synthetic fields to find the synthetic constructors | 431 | // use the synthetic fields to find the synthetic constructors |
| 413 | for( CtConstructor constructor : c.getDeclaredConstructors() ) | 432 | for( CtConstructor constructor : c.getDeclaredConstructors() ) |
| 414 | { | 433 | { |
| 415 | if( !isIllegalConstructor( constructor ) ) | 434 | Set<String> syntheticFieldTypes = Sets.newHashSet(); |
| 435 | if( !isIllegalConstructor( syntheticFieldTypes, constructor ) ) | ||
| 416 | { | 436 | { |
| 417 | continue; | 437 | continue; |
| 418 | } | 438 | } |
| @@ -420,27 +440,57 @@ public class JarIndex | |||
| 420 | ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); | 440 | ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); |
| 421 | ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); | 441 | ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); |
| 422 | 442 | ||
| 443 | // look at the synthetic types to get candidates for the outer class | ||
| 444 | Set<ClassEntry> candidateOuterClasses = Sets.newHashSet(); | ||
| 445 | for( String type : syntheticFieldTypes ) | ||
| 446 | { | ||
| 447 | if( type.startsWith( "L" ) ) | ||
| 448 | { | ||
| 449 | candidateOuterClasses.add( new ClassEntry( type.substring( 1, type.length() - 1 ) ) ); | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | // do we have an answer yet? | ||
| 454 | if( candidateOuterClasses.isEmpty() ) | ||
| 455 | { | ||
| 456 | continue; | ||
| 457 | } | ||
| 458 | else if( candidateOuterClasses.size() == 1 ) | ||
| 459 | { | ||
| 460 | ClassEntry outerClassEntry = candidateOuterClasses.iterator().next(); | ||
| 461 | |||
| 462 | // does this class make sense as an outer class? | ||
| 463 | if( !outerClassEntry.equals( classEntry ) ) | ||
| 464 | { | ||
| 465 | return outerClassEntry.getName(); | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 423 | // who calls this constructor? | 469 | // who calls this constructor? |
| 424 | Set<ClassEntry> callerClasses = Sets.newHashSet(); | 470 | Set<ClassEntry> callerClasses = Sets.newHashSet(); |
| 425 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences( constructorEntry ) ) | 471 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences( constructorEntry ) ) |
| 426 | { | 472 | { |
| 427 | callerClasses.add( reference.context.getClassEntry() ); | 473 | // is that one of our candidates? |
| 474 | if( candidateOuterClasses.contains( reference.context.getClassEntry() ) ) | ||
| 475 | { | ||
| 476 | callerClasses.add( reference.context.getClassEntry() ); | ||
| 477 | } | ||
| 428 | } | 478 | } |
| 429 | 479 | ||
| 430 | // is this called by exactly one class? | 480 | // do we have an answer yet? |
| 431 | if( callerClasses.size() == 1 ) | 481 | if( callerClasses.size() == 1 ) |
| 432 | { | 482 | { |
| 433 | ClassEntry callerClassEntry = callerClasses.iterator().next(); | 483 | ClassEntry outerClassEntry = callerClasses.iterator().next(); |
| 434 | 484 | ||
| 435 | // does this class make sense as an outer class? | 485 | // does this class make sense as an outer class? |
| 436 | if( !callerClassEntry.equals( classEntry ) ) | 486 | if( !outerClassEntry.equals( classEntry ) ) |
| 437 | { | 487 | { |
| 438 | return callerClassEntry.getName(); | 488 | return outerClassEntry.getName(); |
| 439 | } | 489 | } |
| 440 | } | 490 | } |
| 441 | else if( callerClasses.size() > 1 ) | 491 | else |
| 442 | { | 492 | { |
| 443 | System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); | 493 | System.out.println( "WARNING: Unable to choose outer class among options: " + candidateOuterClasses ); |
| 444 | } | 494 | } |
| 445 | } | 495 | } |
| 446 | 496 | ||
| @@ -448,7 +498,7 @@ public class JarIndex | |||
| 448 | } | 498 | } |
| 449 | 499 | ||
| 450 | @SuppressWarnings( "unchecked" ) | 500 | @SuppressWarnings( "unchecked" ) |
| 451 | private boolean isIllegalConstructor( CtConstructor constructor ) | 501 | private boolean isIllegalConstructor( Set<String> syntheticFieldTypes, CtConstructor constructor ) |
| 452 | { | 502 | { |
| 453 | // illegal constructors only set synthetic member fields, then call super() | 503 | // illegal constructors only set synthetic member fields, then call super() |
| 454 | String className = constructor.getDeclaringClass().getName(); | 504 | String className = constructor.getDeclaringClass().getName(); |
| @@ -456,7 +506,6 @@ public class JarIndex | |||
| 456 | // collect all the field accesses, constructor calls, and method calls | 506 | // collect all the field accesses, constructor calls, and method calls |
| 457 | final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); | 507 | final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); |
| 458 | final List<ConstructorCall> constructorCalls = Lists.newArrayList(); | 508 | final List<ConstructorCall> constructorCalls = Lists.newArrayList(); |
| 459 | final List<MethodCall> methodCalls = Lists.newArrayList(); | ||
| 460 | try | 509 | try |
| 461 | { | 510 | { |
| 462 | constructor.instrument( new ExprEditor( ) | 511 | constructor.instrument( new ExprEditor( ) |
| @@ -475,12 +524,6 @@ public class JarIndex | |||
| 475 | { | 524 | { |
| 476 | constructorCalls.add( constructorCall ); | 525 | constructorCalls.add( constructorCall ); |
| 477 | } | 526 | } |
| 478 | |||
| 479 | @Override | ||
| 480 | public void edit( MethodCall methodCall ) | ||
| 481 | { | ||
| 482 | methodCalls.add( methodCall ); | ||
| 483 | } | ||
| 484 | } ); | 527 | } ); |
| 485 | } | 528 | } |
| 486 | catch( CannotCompileException ex ) | 529 | catch( CannotCompileException ex ) |
| @@ -489,25 +532,6 @@ public class JarIndex | |||
| 489 | throw new Error( ex ); | 532 | throw new Error( ex ); |
| 490 | } | 533 | } |
| 491 | 534 | ||
| 492 | // method calls are not allowed | ||
| 493 | if( !methodCalls.isEmpty() ) | ||
| 494 | { | ||
| 495 | return false; | ||
| 496 | } | ||
| 497 | |||
| 498 | // is there only one constructor call? | ||
| 499 | if( constructorCalls.size() != 1 ) | ||
| 500 | { | ||
| 501 | return false; | ||
| 502 | } | ||
| 503 | |||
| 504 | // is the call to super? | ||
| 505 | ConstructorCall constructorCall = constructorCalls.get( 0 ); | ||
| 506 | if( !constructorCall.getMethodName().equals( "super" ) ) | ||
| 507 | { | ||
| 508 | return false; | ||
| 509 | } | ||
| 510 | |||
| 511 | // are there any illegal field writes? | 535 | // are there any illegal field writes? |
| 512 | if( illegalFieldWrites.isEmpty() ) | 536 | if( illegalFieldWrites.isEmpty() ) |
| 513 | { | 537 | { |
| @@ -528,7 +552,7 @@ public class JarIndex | |||
| 528 | FieldInfo fieldInfo = null; | 552 | FieldInfo fieldInfo = null; |
| 529 | for( FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields() ) | 553 | for( FieldInfo info : (List<FieldInfo>)constructor.getDeclaringClass().getClassFile().getFields() ) |
| 530 | { | 554 | { |
| 531 | if( info.getName().equals( fieldWrite.getFieldName() ) ) | 555 | if( info.getName().equals( fieldWrite.getFieldName() ) && info.getDescriptor().equals( fieldWrite.getSignature() ) ) |
| 532 | { | 556 | { |
| 533 | fieldInfo = info; | 557 | fieldInfo = info; |
| 534 | break; | 558 | break; |
| @@ -542,9 +566,13 @@ public class JarIndex | |||
| 542 | 566 | ||
| 543 | // is this field synthetic? | 567 | // is this field synthetic? |
| 544 | boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; | 568 | boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; |
| 545 | if( !isSynthetic ) | 569 | if( isSynthetic ) |
| 570 | { | ||
| 571 | syntheticFieldTypes.add( fieldInfo.getDescriptor() ); | ||
| 572 | } | ||
| 573 | else | ||
| 546 | { | 574 | { |
| 547 | System.err.println( String.format( "WARNING: illegal write to non synthetic field %s.%s", className, fieldInfo.getName() ) ); | 575 | System.err.println( String.format( "WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName() ) ); |
| 548 | return false; | 576 | return false; |
| 549 | } | 577 | } |
| 550 | } | 578 | } |
| @@ -555,15 +583,15 @@ public class JarIndex | |||
| 555 | 583 | ||
| 556 | private boolean isAnonymousClass( CtClass c, String outerClassName ) | 584 | private boolean isAnonymousClass( CtClass c, String outerClassName ) |
| 557 | { | 585 | { |
| 558 | String innerClassName = Descriptor.toJvmName( c.getName() ); | 586 | ClassEntry innerClassEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); |
| 559 | 587 | ||
| 560 | // anonymous classes: | 588 | // anonymous classes: |
| 561 | // can't be abstract | 589 | // can't be abstract |
| 562 | // have only one constructor | 590 | // have only one constructor |
| 563 | // it's called exactly once by the outer class | 591 | // it's called exactly once by the outer class |
| 564 | // type of inner class not referenced anywhere in outer class | 592 | // the type the instance is assigned to can't be this type |
| 565 | 593 | ||
| 566 | // is absract? | 594 | // is abstract? |
| 567 | if( Modifier.isAbstract( c.getModifiers() ) ) | 595 | if( Modifier.isAbstract( c.getModifiers() ) ) |
| 568 | { | 596 | { |
| 569 | return false; | 597 | return false; |
| @@ -577,22 +605,40 @@ public class JarIndex | |||
| 577 | CtConstructor constructor = c.getDeclaredConstructors()[0]; | 605 | CtConstructor constructor = c.getDeclaredConstructors()[0]; |
| 578 | 606 | ||
| 579 | // is this constructor called exactly once? | 607 | // is this constructor called exactly once? |
| 580 | ConstructorEntry constructorEntry = new ConstructorEntry( | 608 | ConstructorEntry constructorEntry = new ConstructorEntry( innerClassEntry, constructor.getMethodInfo().getDescriptor() ); |
| 581 | new ClassEntry( innerClassName ), | 609 | Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = getBehaviorReferences( constructorEntry ); |
| 582 | constructor.getMethodInfo().getDescriptor() | 610 | if( references.size() != 1 ) |
| 583 | ); | ||
| 584 | if( getBehaviorReferences( constructorEntry ).size() != 1 ) | ||
| 585 | { | 611 | { |
| 586 | return false; | 612 | return false; |
| 587 | } | 613 | } |
| 588 | 614 | ||
| 589 | // TODO: check outer class doesn't reference type | 615 | // does the caller use this type? |
| 590 | // except this is hard because we can't just load the outer class now | 616 | BehaviorEntry caller = references.iterator().next().context; |
| 591 | // we'd have to pre-index those references in the JarIndex | 617 | for( FieldEntry fieldEntry : getReferencedFields( caller ) ) |
| 618 | { | ||
| 619 | ClassEntry fieldClass = getFieldClass( fieldEntry ); | ||
| 620 | if( fieldClass != null && fieldClass.equals( innerClassEntry ) ) | ||
| 621 | { | ||
| 622 | // caller references this type, so it can't be anonymous | ||
| 623 | return false; | ||
| 624 | } | ||
| 625 | } | ||
| 626 | for( BehaviorEntry behaviorEntry : getReferencedBehaviors( caller ) ) | ||
| 627 | { | ||
| 628 | // get the class types from the signature | ||
| 629 | for( String className : SignatureUpdater.getClasses( behaviorEntry.getSignature() ) ) | ||
| 630 | { | ||
| 631 | if( className.equals( innerClassEntry.getName() ) ) | ||
| 632 | { | ||
| 633 | // caller references this type, so it can't be anonymous | ||
| 634 | return false; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | } | ||
| 592 | 638 | ||
| 593 | return true; | 639 | return true; |
| 594 | } | 640 | } |
| 595 | 641 | ||
| 596 | public Set<ClassEntry> getObfClassEntries( ) | 642 | public Set<ClassEntry> getObfClassEntries( ) |
| 597 | { | 643 | { |
| 598 | return m_obfClassEntries; | 644 | return m_obfClassEntries; |
| @@ -608,6 +654,11 @@ public class JarIndex | |||
| 608 | return m_access.get( entry ); | 654 | return m_access.get( entry ); |
| 609 | } | 655 | } |
| 610 | 656 | ||
| 657 | public ClassEntry getFieldClass( FieldEntry fieldEntry ) | ||
| 658 | { | ||
| 659 | return m_fieldClasses.get( fieldEntry ); | ||
| 660 | } | ||
| 661 | |||
| 611 | public boolean isMethodImplemented( MethodEntry methodEntry ) | 662 | public boolean isMethodImplemented( MethodEntry methodEntry ) |
| 612 | { | 663 | { |
| 613 | Collection<MethodEntry> implementations = m_methodImplementations.get( methodEntry.getClassName() ); | 664 | Collection<MethodEntry> implementations = m_methodImplementations.get( methodEntry.getClassName() ); |
| @@ -772,11 +823,39 @@ public class JarIndex | |||
| 772 | return m_fieldReferences.get( fieldEntry ); | 823 | return m_fieldReferences.get( fieldEntry ); |
| 773 | } | 824 | } |
| 774 | 825 | ||
| 826 | public Collection<FieldEntry> getReferencedFields( BehaviorEntry behaviorEntry ) | ||
| 827 | { | ||
| 828 | // linear search is fast enough for now | ||
| 829 | Set<FieldEntry> fieldEntries = Sets.newHashSet(); | ||
| 830 | for( EntryReference<FieldEntry,BehaviorEntry> reference : m_fieldReferences.values() ) | ||
| 831 | { | ||
| 832 | if( reference.context == behaviorEntry ) | ||
| 833 | { | ||
| 834 | fieldEntries.add( reference.entry ); | ||
| 835 | } | ||
| 836 | } | ||
| 837 | return fieldEntries; | ||
| 838 | } | ||
| 839 | |||
| 775 | public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences( BehaviorEntry behaviorEntry ) | 840 | public Collection<EntryReference<BehaviorEntry,BehaviorEntry>> getBehaviorReferences( BehaviorEntry behaviorEntry ) |
| 776 | { | 841 | { |
| 777 | return m_behaviorReferences.get( behaviorEntry ); | 842 | return m_behaviorReferences.get( behaviorEntry ); |
| 778 | } | 843 | } |
| 779 | 844 | ||
| 845 | public Collection<BehaviorEntry> getReferencedBehaviors( BehaviorEntry behaviorEntry ) | ||
| 846 | { | ||
| 847 | // linear search is fast enough for now | ||
| 848 | Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); | ||
| 849 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : m_behaviorReferences.values() ) | ||
| 850 | { | ||
| 851 | if( reference.context == behaviorEntry ) | ||
| 852 | { | ||
| 853 | behaviorEntries.add( reference.entry ); | ||
| 854 | } | ||
| 855 | } | ||
| 856 | return behaviorEntries; | ||
| 857 | } | ||
| 858 | |||
| 780 | public Collection<String> getInnerClasses( String obfOuterClassName ) | 859 | public Collection<String> getInnerClasses( String obfOuterClassName ) |
| 781 | { | 860 | { |
| 782 | return m_innerClasses.get( obfOuterClassName ); | 861 | return m_innerClasses.get( obfOuterClassName ); |
diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java index 4c0dbac..528a743 100644 --- a/src/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java | |||
| @@ -12,6 +12,9 @@ package cuchaz.enigma.mapping; | |||
| 12 | 12 | ||
| 13 | import java.io.IOException; | 13 | import java.io.IOException; |
| 14 | import java.io.StringReader; | 14 | import java.io.StringReader; |
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import com.beust.jcommander.internal.Lists; | ||
| 15 | 18 | ||
| 16 | public class SignatureUpdater | 19 | public class SignatureUpdater |
| 17 | { | 20 | { |
| @@ -84,4 +87,19 @@ public class SignatureUpdater | |||
| 84 | 87 | ||
| 85 | return null; | 88 | return null; |
| 86 | } | 89 | } |
| 90 | |||
| 91 | public static List<String> getClasses( String signature ) | ||
| 92 | { | ||
| 93 | final List<String> classNames = Lists.newArrayList(); | ||
| 94 | update( signature, new ClassNameUpdater( ) | ||
| 95 | { | ||
| 96 | @Override | ||
| 97 | public String update( String className ) | ||
| 98 | { | ||
| 99 | classNames.add( className ); | ||
| 100 | return className; | ||
| 101 | } | ||
| 102 | } ); | ||
| 103 | return classNames; | ||
| 104 | } | ||
| 87 | } | 105 | } |
diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 3bb0c48..71de24a 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | ******************************************************************************/ | 11 | ******************************************************************************/ |
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import static org.junit.Assert.assertEquals; | 14 | import static org.junit.Assert.*; |
| 15 | 15 | ||
| 16 | import java.io.File; | 16 | import java.io.File; |
| 17 | import java.io.IOException; | 17 | import java.io.IOException; |
diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java new file mode 100644 index 0000000..c6b1b5f --- /dev/null +++ b/test/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2014 Jeff Martin. | ||
| 3 | * | ||
| 4 | * All rights reserved. This program and the accompanying materials | ||
| 5 | * are made available under the terms of the GNU Public License v3.0 | ||
| 6 | * which accompanies this distribution, and is available at | ||
| 7 | * http://www.gnu.org/licenses/gpl.html | ||
| 8 | * | ||
| 9 | * Contributors: | ||
| 10 | * Jeff Martin - initial API and implementation | ||
| 11 | ******************************************************************************/ | ||
| 12 | package cuchaz.enigma; | ||
| 13 | |||
| 14 | import static org.hamcrest.MatcherAssert.*; | ||
| 15 | import static org.hamcrest.Matchers.*; | ||
| 16 | |||
| 17 | import java.util.jar.JarFile; | ||
| 18 | |||
| 19 | import org.junit.Test; | ||
| 20 | |||
| 21 | import cuchaz.enigma.analysis.JarIndex; | ||
| 22 | |||
| 23 | public class TestInnerClasses | ||
| 24 | { | ||
| 25 | private JarIndex m_index; | ||
| 26 | |||
| 27 | private static final String AnonymousOuter = "none/a"; | ||
| 28 | private static final String AnonymousInner = "none/b"; | ||
| 29 | private static final String SimpleOuter = "none/e"; | ||
| 30 | private static final String SimpleInner = "none/f"; | ||
| 31 | private static final String ConstructorArgsOuter = "none/c"; | ||
| 32 | private static final String ConstructorArgsInner = "none/d"; | ||
| 33 | |||
| 34 | public TestInnerClasses( ) | ||
| 35 | throws Exception | ||
| 36 | { | ||
| 37 | m_index = new JarIndex(); | ||
| 38 | m_index.indexJar( new JarFile( "build/libs/testInnerClasses.obf.jar" ), true ); | ||
| 39 | } | ||
| 40 | |||
| 41 | @Test | ||
| 42 | public void simple( ) | ||
| 43 | { | ||
| 44 | assertThat( m_index.getOuterClass( SimpleInner ), is( SimpleOuter ) ); | ||
| 45 | assertThat( m_index.getInnerClasses( SimpleOuter ), containsInAnyOrder( SimpleInner ) ); | ||
| 46 | assertThat( m_index.isAnonymousClass( SimpleInner ), is( false ) ); | ||
| 47 | } | ||
| 48 | |||
| 49 | @Test | ||
| 50 | public void anonymous( ) | ||
| 51 | { | ||
| 52 | assertThat( m_index.getOuterClass( AnonymousInner ), is( AnonymousOuter ) ); | ||
| 53 | assertThat( m_index.getInnerClasses( AnonymousOuter ), containsInAnyOrder( AnonymousInner ) ); | ||
| 54 | assertThat( m_index.isAnonymousClass( AnonymousInner ), is( true ) ); | ||
| 55 | } | ||
| 56 | |||
| 57 | @Test | ||
| 58 | public void constructorArgs( ) | ||
| 59 | { | ||
| 60 | assertThat( m_index.getOuterClass( ConstructorArgsInner ), is( ConstructorArgsOuter ) ); | ||
| 61 | assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); | ||
| 62 | assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java index c069188..0238171 100644 --- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java | |||
| @@ -10,13 +10,9 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | 11 | package cuchaz.enigma; |
| 12 | 12 | ||
| 13 | import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; | 13 | import static cuchaz.enigma.EntryFactory.*; |
| 14 | import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; | 14 | import static org.hamcrest.MatcherAssert.*; |
| 15 | import static cuchaz.enigma.EntryFactory.newClass; | 15 | import static org.hamcrest.Matchers.*; |
| 16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 17 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 18 | import static org.hamcrest.Matchers.empty; | ||
| 19 | import static org.hamcrest.Matchers.is; | ||
| 20 | 16 | ||
| 21 | import java.io.File; | 17 | import java.io.File; |
| 22 | import java.util.Collection; | 18 | import java.util.Collection; |
diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java index 3d488ee..317a6bc 100644 --- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java | |||
| @@ -10,17 +10,9 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | 11 | package cuchaz.enigma; |
| 12 | 12 | ||
| 13 | import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByConstructor; | 13 | import static cuchaz.enigma.EntryFactory.*; |
| 14 | import static cuchaz.enigma.EntryFactory.newBehaviorReferenceByMethod; | 14 | import static org.hamcrest.MatcherAssert.*; |
| 15 | import static cuchaz.enigma.EntryFactory.newClass; | 15 | import static org.hamcrest.Matchers.*; |
| 16 | import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; | ||
| 17 | import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; | ||
| 18 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 19 | import static org.hamcrest.Matchers.contains; | ||
| 20 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 21 | import static org.hamcrest.Matchers.empty; | ||
| 22 | import static org.hamcrest.Matchers.is; | ||
| 23 | import static org.hamcrest.Matchers.nullValue; | ||
| 24 | 16 | ||
| 25 | import java.util.Collection; | 17 | import java.util.Collection; |
| 26 | import java.util.Set; | 18 | import java.util.Set; |
diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java index 9236d0c..4c32b70 100644 --- a/test/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java | |||
| @@ -11,17 +11,9 @@ | |||
| 11 | ******************************************************************************/ | 11 | ******************************************************************************/ |
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import static cuchaz.enigma.EntryFactory.newClass; | 14 | import static cuchaz.enigma.EntryFactory.*; |
| 15 | import static cuchaz.enigma.EntryFactory.newField; | 15 | import static org.hamcrest.MatcherAssert.*; |
| 16 | import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; | 16 | import static org.hamcrest.Matchers.*; |
| 17 | import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; | ||
| 18 | import static cuchaz.enigma.EntryFactory.newMethod; | ||
| 19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 20 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 21 | import static org.hamcrest.Matchers.empty; | ||
| 22 | import static org.hamcrest.Matchers.is; | ||
| 23 | import static org.hamcrest.Matchers.not; | ||
| 24 | import static org.hamcrest.Matchers.nullValue; | ||
| 25 | 17 | ||
| 26 | import java.util.Collection; | 18 | import java.util.Collection; |
| 27 | import java.util.Set; | 19 | import java.util.Set; |
diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java new file mode 100644 index 0000000..dbff523 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | ||
| 2 | |||
| 3 | public class Anonymous // a | ||
| 4 | { | ||
| 5 | public void foo( ) | ||
| 6 | { | ||
| 7 | Runnable runnable = new Runnable( ) // b | ||
| 8 | { | ||
| 9 | @Override | ||
| 10 | public void run( ) | ||
| 11 | { | ||
| 12 | // don't care | ||
| 13 | } | ||
| 14 | }; | ||
| 15 | runnable.run(); | ||
| 16 | } | ||
| 17 | } | ||
diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java new file mode 100644 index 0000000..d12d9cf --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | ||
| 2 | |||
| 3 | @SuppressWarnings( "unused" ) | ||
| 4 | public class ConstructorArgs // c | ||
| 5 | { | ||
| 6 | class Inner // d | ||
| 7 | { | ||
| 8 | private int a; | ||
| 9 | |||
| 10 | public Inner( int a ) | ||
| 11 | { | ||
| 12 | this.a = a; | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | Inner i; | ||
| 17 | |||
| 18 | public void foo( ) | ||
| 19 | { | ||
| 20 | i = new Inner( 5 ); | ||
| 21 | } | ||
| 22 | } | ||
diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java new file mode 100644 index 0000000..f2c08a7 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | ||
| 2 | |||
| 3 | public class Simple // e | ||
| 4 | { | ||
| 5 | class Inner // f | ||
| 6 | { | ||
| 7 | // nothing to do | ||
| 8 | } | ||
| 9 | } | ||