summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java95
-rw-r--r--test/cuchaz/enigma/TestInnerClasses.java18
-rw-r--r--test/cuchaz/enigma/inputs/innerClasses/Anonymous.java4
-rw-r--r--test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java16
-rw-r--r--test/cuchaz/enigma/inputs/innerClasses/ConstructorArgs.java4
-rw-r--r--test/cuchaz/enigma/inputs/innerClasses/Simple.java4
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 @@
1package cuchaz.enigma.inputs.innerClasses; 1package cuchaz.enigma.inputs.innerClasses;
2 2
3public class Anonymous // a 3public 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 @@
1package cuchaz.enigma.inputs.innerClasses;
2
3public 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 @@
1package cuchaz.enigma.inputs.innerClasses; 1package cuchaz.enigma.inputs.innerClasses;
2 2
3@SuppressWarnings( "unused" ) 3@SuppressWarnings( "unused" )
4public class ConstructorArgs // c 4public 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 @@
1package cuchaz.enigma.inputs.innerClasses; 1package cuchaz.enigma.inputs.innerClasses;
2 2
3public class Simple // e 3public class Simple
4{ 4{
5 class Inner // f 5 class Inner
6 { 6 {
7 // nothing to do 7 // nothing to do
8 } 8 }