From df06f4ddde5e255750edc4087cfba54823404909 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 21 Sep 2014 22:08:05 -0400 Subject: improved inner/anonymous class detection --- src/cuchaz/enigma/analysis/JarIndex.java | 95 +++++++++++++++------- test/cuchaz/enigma/TestInnerClasses.java | 18 +++- .../enigma/inputs/innerClasses/Anonymous.java | 4 +- .../innerClasses/AnonymousWithScopeArgs.java | 16 ++++ .../inputs/innerClasses/ConstructorArgs.java | 4 +- test/cuchaz/enigma/inputs/innerClasses/Simple.java | 4 +- 6 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 test/cuchaz/enigma/inputs/innerClasses/AnonymousWithScopeArgs.java 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 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); ConstructorEntry constructorEntry = new ConstructorEntry( classEntry, constructor.getMethodInfo().getDescriptor() ); - // look at the synthetic types to get candidates for the outer class - Set candidateOuterClasses = Sets.newHashSet(); + // gather the classes from the illegally-set synthetic fields + Set illegallySetClasses = Sets.newHashSet(); for( String type : syntheticFieldTypes ) { if( type.startsWith( "L" ) ) { - candidateOuterClasses.add( new ClassEntry( type.substring( 1, type.length() - 1 ) ) ); - } - } - - // do we have an answer yet? - if( candidateOuterClasses.isEmpty() ) - { - continue; - } - else if( candidateOuterClasses.size() == 1 ) - { - ClassEntry outerClassEntry = candidateOuterClasses.iterator().next(); - - // does this class make sense as an outer class? - if( !outerClassEntry.equals( classEntry ) ) - { - return outerClassEntry.getName(); + ClassEntry outerClassEntry = new ClassEntry( type.substring( 1, type.length() - 1 ) ); + if( isSaneOuterClass( outerClassEntry, classEntry ) ) + { + illegallySetClasses.add( outerClassEntry ); + } } } @@ -470,33 +458,82 @@ public class JarIndex Set callerClasses = Sets.newHashSet(); for( EntryReference reference : getBehaviorReferences( constructorEntry ) ) { - // is that one of our candidates? - if( candidateOuterClasses.contains( reference.context.getClassEntry() ) ) + // make sure it's not a call to super + if( reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry ) + { + // is the entry a superclass of the context? + String calledClassName = reference.entry.getClassName(); + String callerSuperclassName = m_translationIndex.getSuperclassName( reference.context.getClassName() ); + if( callerSuperclassName != null && callerSuperclassName.equals( calledClassName ) ) + { + // it's a super call, skip + continue; + } + } + + if( isSaneOuterClass( reference.context.getClassEntry(), classEntry ) ) { - callerClasses.add( reference.context.getClassEntry() ); + callerClasses.add( reference.context.getClassEntry() ); } } // do we have an answer yet? - if( callerClasses.size() == 1 ) + if( callerClasses.isEmpty() ) { - ClassEntry outerClassEntry = callerClasses.iterator().next(); - - // does this class make sense as an outer class? - if( !outerClassEntry.equals( classEntry ) ) + if( illegallySetClasses.size() == 1 ) { - return outerClassEntry.getName(); + return illegallySetClasses.iterator().next().getName(); + } + else + { + System.out.println( String.format( "WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry ) ); } } else { - System.out.println( "WARNING: Unable to choose outer class among options: " + candidateOuterClasses ); + if( callerClasses.size() == 1 ) + { + return callerClasses.iterator().next().getName(); + } + else + { + // multiple callers, do the illegally set classes narrow it down? + Set intersection = Sets.newHashSet( callerClasses ); + intersection.retainAll( illegallySetClasses ); + if( intersection.size() == 1 ) + { + return intersection.iterator().next().getName(); + } + else + { + System.out.println( String.format( "WARNING: Unable to choose outer class for %s among options: %s", + classEntry, callerClasses + ) ); + } + } } } return null; } + private boolean isSaneOuterClass( ClassEntry outerClassEntry, ClassEntry innerClassEntry ) + { + // clearly this would be silly + if( outerClassEntry.equals( innerClassEntry ) ) + { + return false; + } + + // is the outer class in the jar? + if( !m_obfClassEntries.contains( outerClassEntry ) ) + { + return false; + } + + return true; + } + @SuppressWarnings( "unchecked" ) private boolean isIllegalConstructor( Set syntheticFieldTypes, CtConstructor constructor ) { 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 private static final String AnonymousOuter = "none/a"; private static final String AnonymousInner = "none/b"; - private static final String SimpleOuter = "none/e"; - private static final String SimpleInner = "none/f"; - private static final String ConstructorArgsOuter = "none/c"; - private static final String ConstructorArgsInner = "none/d"; + private static final String SimpleOuter = "none/g"; + private static final String SimpleInner = "none/h"; + private static final String ConstructorArgsOuter = "none/e"; + private static final String ConstructorArgsInner = "none/f"; + private static final String AnonymousWithScopeArgsOuter = "none/c"; + private static final String AnonymousWithScopeArgsInner = "none/d"; public TestInnerClasses( ) throws Exception @@ -61,4 +63,12 @@ public class TestInnerClasses assertThat( m_index.getInnerClasses( ConstructorArgsOuter ), containsInAnyOrder( ConstructorArgsInner ) ); assertThat( m_index.isAnonymousClass( ConstructorArgsInner ), is( false ) ); } + + @Test + public void anonymousWithScopeArgs( ) + { + assertThat( m_index.getOuterClass( AnonymousWithScopeArgsInner ), is( AnonymousWithScopeArgsOuter ) ); + assertThat( m_index.getInnerClasses( AnonymousWithScopeArgsOuter ), containsInAnyOrder( AnonymousWithScopeArgsInner ) ); + assertThat( m_index.isAnonymousClass( AnonymousWithScopeArgsInner ), is( true ) ); + } } 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 @@ package cuchaz.enigma.inputs.innerClasses; -public class Anonymous // a +public class Anonymous { public void foo( ) { - Runnable runnable = new Runnable( ) // b + Runnable runnable = new Runnable( ) { @Override 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 @@ +package cuchaz.enigma.inputs.innerClasses; + +public class AnonymousWithScopeArgs +{ + public static void foo( final Simple arg ) + { + System.out.println( new Object( ) + { + @Override + public String toString( ) + { + return arg.toString(); + } + } ); + } +} 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 @@ package cuchaz.enigma.inputs.innerClasses; @SuppressWarnings( "unused" ) -public class ConstructorArgs // c +public class ConstructorArgs { - class Inner // d + class Inner { private int a; 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 @@ package cuchaz.enigma.inputs.innerClasses; -public class Simple // e +public class Simple { - class Inner // f + class Inner { // nothing to do } -- cgit v1.2.3