diff options
6 files changed, 102 insertions, 39 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 43e2bf62..c36e9cb0 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -440,29 +440,17 @@ public class JarIndex | |||
| 440 | ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); | 440 | ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); |
| 441 | ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); | 441 | ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); |
| 442 | 442 | ||
| 443 | // look at the synthetic types to get candidates for the outer class | 443 | // gather the classes from the illegally-set synthetic fields |
| 444 | Set<ClassEntry> candidateOuterClasses = Sets.newHashSet(); | 444 | Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); |
| 445 | for( String type : syntheticFieldTypes ) | 445 | for( String type : syntheticFieldTypes ) |
| 446 | { | 446 | { |
| 447 | if( type.startsWith( "L" ) ) | 447 | if( type.startsWith( "L" ) ) |
| 448 | { | 448 | { |
| 449 | candidateOuterClasses.add( new ClassEntry( type.substring( 1, type.length() - 1 ) ) ); | 449 | ClassEntry outerClassEntry = new ClassEntry( type.substring( 1, type.length() - 1 ) ); |
| 450 | } | 450 | if( isSaneOuterClass( outerClassEntry, classEntry ) ) |
| 451 | } | 451 | { |
| 452 | 452 | illegallySetClasses.add( outerClassEntry ); | |
| 453 | // do we have an answer yet? | 453 | } |
| 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 | } | 454 | } |
| 467 | } | 455 | } |
| 468 | 456 | ||
| @@ -470,33 +458,82 @@ public class JarIndex | |||
| 470 | Set<ClassEntry> callerClasses = Sets.newHashSet(); | 458 | Set<ClassEntry> callerClasses = Sets.newHashSet(); |
| 471 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences( constructorEntry ) ) | 459 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : getBehaviorReferences( constructorEntry ) ) |
| 472 | { | 460 | { |
| 473 | // is that one of our candidates? | 461 | // make sure it's not a call to super |
| 474 | if( candidateOuterClasses.contains( reference.context.getClassEntry() ) ) | 462 | if( reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry ) |
| 463 | { | ||
| 464 | // is the entry a superclass of the context? | ||
| 465 | String calledClassName = reference.entry.getClassName(); | ||
| 466 | String callerSuperclassName = m_translationIndex.getSuperclassName( reference.context.getClassName() ); | ||
| 467 | if( callerSuperclassName != null && callerSuperclassName.equals( calledClassName ) ) | ||
| 468 | { | ||
| 469 | // it's a super call, skip | ||
| 470 | continue; | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | if( isSaneOuterClass( reference.context.getClassEntry(), classEntry ) ) | ||
| 475 | { | 475 | { |
| 476 | callerClasses.add( reference.context.getClassEntry() ); | 476 | callerClasses.add( reference.context.getClassEntry() ); |
| 477 | } | 477 | } |
| 478 | } | 478 | } |
| 479 | 479 | ||
| 480 | // do we have an answer yet? | 480 | // do we have an answer yet? |
| 481 | if( callerClasses.size() == 1 ) | 481 | if( callerClasses.isEmpty() ) |
| 482 | { | 482 | { |
| 483 | ClassEntry outerClassEntry = callerClasses.iterator().next(); | 483 | if( illegallySetClasses.size() == 1 ) |
| 484 | |||
| 485 | // does this class make sense as an outer class? | ||
| 486 | if( !outerClassEntry.equals( classEntry ) ) | ||
| 487 | { | 484 | { |
| 488 | return outerClassEntry.getName(); | 485 | return illegallySetClasses.iterator().next().getName(); |
| 486 | } | ||
| 487 | else | ||
| 488 | { | ||
| 489 | System.out.println( String.format( "WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry ) ); | ||
| 489 | } | 490 | } |
| 490 | } | 491 | } |
| 491 | else | 492 | else |
| 492 | { | 493 | { |
| 493 | System.out.println( "WARNING: Unable to choose outer class among options: " + candidateOuterClasses ); | 494 | if( callerClasses.size() == 1 ) |
| 495 | { | ||
| 496 | return callerClasses.iterator().next().getName(); | ||
| 497 | } | ||
| 498 | else | ||
| 499 | { | ||
| 500 | // multiple callers, do the illegally set classes narrow it down? | ||
| 501 | Set<ClassEntry> intersection = Sets.newHashSet( callerClasses ); | ||
| 502 | intersection.retainAll( illegallySetClasses ); | ||
| 503 | if( intersection.size() == 1 ) | ||
| 504 | { | ||
| 505 | return intersection.iterator().next().getName(); | ||
| 506 | } | ||
| 507 | else | ||
| 508 | { | ||
| 509 | System.out.println( String.format( "WARNING: Unable to choose outer class for %s among options: %s", | ||
| 510 | classEntry, callerClasses | ||
| 511 | ) ); | ||
| 512 | } | ||
| 513 | } | ||
| 494 | } | 514 | } |
| 495 | } | 515 | } |
| 496 | 516 | ||
| 497 | return null; | 517 | return null; |
| 498 | } | 518 | } |
| 499 | 519 | ||
| 520 | private boolean isSaneOuterClass( ClassEntry outerClassEntry, ClassEntry innerClassEntry ) | ||
| 521 | { | ||
| 522 | // clearly this would be silly | ||
| 523 | if( outerClassEntry.equals( innerClassEntry ) ) | ||
| 524 | { | ||
| 525 | return false; | ||
| 526 | } | ||
| 527 | |||
| 528 | // is the outer class in the jar? | ||
| 529 | if( !m_obfClassEntries.contains( outerClassEntry ) ) | ||
| 530 | { | ||
| 531 | return false; | ||
| 532 | } | ||
| 533 | |||
| 534 | return true; | ||
| 535 | } | ||
| 536 | |||
| 500 | @SuppressWarnings( "unchecked" ) | 537 | @SuppressWarnings( "unchecked" ) |
| 501 | private boolean isIllegalConstructor( Set<String> syntheticFieldTypes, CtConstructor constructor ) | 538 | private boolean isIllegalConstructor( Set<String> syntheticFieldTypes, CtConstructor constructor ) |
| 502 | { | 539 | { |
diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java index c6b1b5fb..e555d920 100644 --- a/test/cuchaz/enigma/TestInnerClasses.java +++ b/test/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -26,10 +26,12 @@ public class TestInnerClasses | |||
| 26 | 26 | ||
| 27 | private static final String AnonymousOuter = "none/a"; | 27 | private static final String AnonymousOuter = "none/a"; |
| 28 | private static final String AnonymousInner = "none/b"; | 28 | private static final String AnonymousInner = "none/b"; |
| 29 | private static final String SimpleOuter = "none/e"; | 29 | private static final String SimpleOuter = "none/g"; |
| 30 | private static final String SimpleInner = "none/f"; | 30 | private static final String SimpleInner = "none/h"; |
| 31 | private static final String ConstructorArgsOuter = "none/c"; | 31 | private static final String ConstructorArgsOuter = "none/e"; |
| 32 | private static final String ConstructorArgsInner = "none/d"; | 32 | private static final String ConstructorArgsInner = "none/f"; |
| 33 | private static final String AnonymousWithScopeArgsOuter = "none/c"; | ||
| 34 | private static final String AnonymousWithScopeArgsInner = "none/d"; | ||
| 33 | 35 | ||
| 34 | public TestInnerClasses( ) | 36 | public TestInnerClasses( ) |
| 35 | throws Exception | 37 | throws Exception |
| @@ -61,4 +63,12 @@ public class TestInnerClasses | |||
| 61 | assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); | 63 | assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); |
| 62 | assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); | 64 | assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); |
| 63 | } | 65 | } |
| 66 | |||
| 67 | @Test | ||
| 68 | public void anonymousWithScopeArgs( ) | ||
| 69 | { | ||
| 70 | assertThat( m_index.getOuterClass( AnonymousWithScopeArgsInner ), is( AnonymousWithScopeArgsOuter ) ); | ||
| 71 | assertThat( m_index.getInnerClasses( AnonymousWithScopeArgsOuter ), containsInAnyOrder( AnonymousWithScopeArgsInner ) ); | ||
| 72 | assertThat( m_index.isAnonymousClass( AnonymousWithScopeArgsInner ), is( true ) ); | ||
| 73 | } | ||
| 64 | } | 74 | } |
diff --git a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java index dbff5238..d36a514c 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Anonymous.java | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | 1 | package cuchaz.enigma.inputs.innerClasses; |
| 2 | 2 | ||
| 3 | public class Anonymous // a | 3 | public class Anonymous |
| 4 | { | 4 | { |
| 5 | public void foo( ) | 5 | public void foo( ) |
| 6 | { | 6 | { |
| 7 | Runnable runnable = new Runnable( ) // b | 7 | Runnable runnable = new Runnable( ) |
| 8 | { | 8 | { |
| 9 | @Override | 9 | @Override |
| 10 | public void run( ) | 10 | public void run( ) |
diff --git a/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java new file mode 100644 index 00000000..e0a65e25 --- /dev/null +++ b/test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | ||
| 2 | |||
| 3 | public class AnonymousWithScopeArgs | ||
| 4 | { | ||
| 5 | public static void foo( final Simple arg ) | ||
| 6 | { | ||
| 7 | System.out.println( new Object( ) | ||
| 8 | { | ||
| 9 | @Override | ||
| 10 | public String toString( ) | ||
| 11 | { | ||
| 12 | return arg.toString(); | ||
| 13 | } | ||
| 14 | } ); | ||
| 15 | } | ||
| 16 | } | ||
diff --git a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java index d12d9cf7..e24395c7 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java +++ b/test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | 1 | package cuchaz.enigma.inputs.innerClasses; |
| 2 | 2 | ||
| 3 | @SuppressWarnings( "unused" ) | 3 | @SuppressWarnings( "unused" ) |
| 4 | public class ConstructorArgs // c | 4 | public class ConstructorArgs |
| 5 | { | 5 | { |
| 6 | class Inner // d | 6 | class Inner |
| 7 | { | 7 | { |
| 8 | private int a; | 8 | private int a; |
| 9 | 9 | ||
diff --git a/test/cuchaz/enigma/inputs/innerClasses/Simple.java b/test/cuchaz/enigma/inputs/innerClasses/Simple.java index f2c08a73..405c6399 100644 --- a/test/cuchaz/enigma/inputs/innerClasses/Simple.java +++ b/test/cuchaz/enigma/inputs/innerClasses/Simple.java | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | package cuchaz.enigma.inputs.innerClasses; | 1 | package cuchaz.enigma.inputs.innerClasses; |
| 2 | 2 | ||
| 3 | public class Simple // e | 3 | public class Simple |
| 4 | { | 4 | { |
| 5 | class Inner // f | 5 | class Inner |
| 6 | { | 6 | { |
| 7 | // nothing to do | 7 | // nothing to do |
| 8 | } | 8 | } |