summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java181
-rw-r--r--src/cuchaz/enigma/mapping/SignatureUpdater.java18
2 files changed, 148 insertions, 51 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 6c955bcd..43e2bf62 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -48,6 +48,7 @@ import cuchaz.enigma.mapping.ConstructorEntry;
48import cuchaz.enigma.mapping.Entry; 48import cuchaz.enigma.mapping.Entry;
49import cuchaz.enigma.mapping.FieldEntry; 49import cuchaz.enigma.mapping.FieldEntry;
50import cuchaz.enigma.mapping.MethodEntry; 50import cuchaz.enigma.mapping.MethodEntry;
51import cuchaz.enigma.mapping.SignatureUpdater;
51import cuchaz.enigma.mapping.Translator; 52import cuchaz.enigma.mapping.Translator;
52 53
53public class JarIndex 54public 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 4c0dbac1..528a7437 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
13import java.io.IOException; 13import java.io.IOException;
14import java.io.StringReader; 14import java.io.StringReader;
15import java.util.List;
16
17import com.beust.jcommander.internal.Lists;
15 18
16public class SignatureUpdater 19public 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}