summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/JarIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/analysis/JarIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java240
1 files changed, 63 insertions, 177 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index e7c92be..a8ac001 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -11,9 +11,8 @@
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.lang.reflect.Modifier; 13import java.lang.reflect.Modifier;
14import java.util.AbstractMap;
15import java.util.Collection; 14import java.util.Collection;
16import java.util.Iterator; 15import java.util.HashSet;
17import java.util.List; 16import java.util.List;
18import java.util.Map; 17import java.util.Map;
19import java.util.Set; 18import java.util.Set;
@@ -43,7 +42,6 @@ import com.google.common.collect.Sets;
43 42
44import cuchaz.enigma.Constants; 43import cuchaz.enigma.Constants;
45import cuchaz.enigma.bytecode.ClassRenamer; 44import cuchaz.enigma.bytecode.ClassRenamer;
46import cuchaz.enigma.mapping.ArgumentEntry;
47import cuchaz.enigma.mapping.BehaviorEntry; 45import cuchaz.enigma.mapping.BehaviorEntry;
48import cuchaz.enigma.mapping.ClassEntry; 46import cuchaz.enigma.mapping.ClassEntry;
49import cuchaz.enigma.mapping.ConstructorEntry; 47import cuchaz.enigma.mapping.ConstructorEntry;
@@ -55,7 +53,8 @@ import cuchaz.enigma.mapping.Translator;
55public class JarIndex 53public class JarIndex
56{ 54{
57 private Set<ClassEntry> m_obfClassEntries; 55 private Set<ClassEntry> m_obfClassEntries;
58 private Ancestries m_ancestries; 56 private TranslationIndex m_translationIndex;
57 private Multimap<String,String> m_interfaces;
59 private Map<Entry,Access> m_access; 58 private Map<Entry,Access> m_access;
60 private Multimap<String,MethodEntry> m_methodImplementations; 59 private Multimap<String,MethodEntry> m_methodImplementations;
61 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; 60 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences;
@@ -68,7 +67,8 @@ public class JarIndex
68 public JarIndex( ) 67 public JarIndex( )
69 { 68 {
70 m_obfClassEntries = Sets.newHashSet(); 69 m_obfClassEntries = Sets.newHashSet();
71 m_ancestries = new Ancestries(); 70 m_translationIndex = new TranslationIndex();
71 m_interfaces = HashMultimap.create();
72 m_access = Maps.newHashMap(); 72 m_access = Maps.newHashMap();
73 m_methodImplementations = HashMultimap.create(); 73 m_methodImplementations = HashMultimap.create();
74 m_behaviorReferences = HashMultimap.create(); 74 m_behaviorReferences = HashMultimap.create();
@@ -109,15 +109,25 @@ public class JarIndex
109 } 109 }
110 } 110 }
111 111
112 // step 3: index the types, methods 112 // step 3: index extends, implements, fields, and methods
113 for( CtClass c : JarClassIterator.classes( jar ) ) 113 for( CtClass c : JarClassIterator.classes( jar ) )
114 { 114 {
115 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); 115 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage );
116 String className = Descriptor.toJvmName( c.getName() ); 116 String className = Descriptor.toJvmName( c.getName() );
117 m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); 117 m_translationIndex.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) );
118 for( String interfaceName : c.getClassFile().getInterfaces() ) 118 for( String interfaceName : c.getClassFile().getInterfaces() )
119 { 119 {
120 m_ancestries.addInterface( className, Descriptor.toJvmName( interfaceName ) ); 120 className = Descriptor.toJvmName( className );
121 interfaceName = Descriptor.toJvmName( interfaceName );
122 if( className.equals( interfaceName ) )
123 {
124 throw new IllegalArgumentException( "Class cannot be its own interface! " + className );
125 }
126 m_interfaces.put( className, interfaceName );
127 }
128 for( CtField field : c.getDeclaredFields() )
129 {
130 indexField( field );
121 } 131 }
122 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 132 for( CtBehavior behavior : c.getDeclaredBehaviors() )
123 { 133 {
@@ -159,11 +169,24 @@ public class JarIndex
159 { 169 {
160 renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); 170 renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() );
161 } 171 }
162 renameClasses( renames ); 172 EntryRenamer.renameClassesInSet( renames, m_obfClassEntries );
173 m_translationIndex.renameClasses( renames );
174 EntryRenamer.renameClassesInMultimap( renames, m_interfaces );
175 EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations );
176 EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences );
177 EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences );
163 } 178 }
164 179
165 // step 5: update other indices with bridge method info 180 // step 6: update other indices with bridge method info
166 renameMethods( m_bridgeMethods ); 181 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations );
182 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences );
183 EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences );
184 }
185
186 private void indexField( CtField field )
187 {
188 String className = Descriptor.toJvmName( field.getDeclaringClass().getName() );
189 m_translationIndex.addField( className, field.getName() );
167 } 190 }
168 191
169 private void indexBehavior( CtBehavior behavior ) 192 private void indexBehavior( CtBehavior behavior )
@@ -528,9 +551,9 @@ public class JarIndex
528 return m_obfClassEntries; 551 return m_obfClassEntries;
529 } 552 }
530 553
531 public Ancestries getAncestries( ) 554 public TranslationIndex getTranslationIndex( )
532 { 555 {
533 return m_ancestries; 556 return m_translationIndex;
534 } 557 }
535 558
536 public Access getAccess( Entry entry ) 559 public Access getAccess( Entry entry )
@@ -553,11 +576,11 @@ public class JarIndex
553 // get the root node 576 // get the root node
554 List<String> ancestry = Lists.newArrayList(); 577 List<String> ancestry = Lists.newArrayList();
555 ancestry.add( obfClassEntry.getName() ); 578 ancestry.add( obfClassEntry.getName() );
556 ancestry.addAll( m_ancestries.getAncestry( obfClassEntry.getName() ) ); 579 ancestry.addAll( m_translationIndex.getAncestry( obfClassEntry.getName() ) );
557 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); 580 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) );
558 581
559 // expand all children recursively 582 // expand all children recursively
560 rootNode.load( m_ancestries, true ); 583 rootNode.load( m_translationIndex, true );
561 584
562 return rootNode; 585 return rootNode;
563 } 586 }
@@ -565,7 +588,7 @@ public class JarIndex
565 public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) 588 public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry )
566 { 589 {
567 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); 590 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry );
568 node.load( m_ancestries ); 591 node.load( this );
569 return node; 592 return node;
570 } 593 }
571 594
@@ -573,7 +596,7 @@ public class JarIndex
573 { 596 {
574 // travel to the ancestor implementation 597 // travel to the ancestor implementation
575 String baseImplementationClassName = obfMethodEntry.getClassName(); 598 String baseImplementationClassName = obfMethodEntry.getClassName();
576 for( String ancestorClassName : m_ancestries.getAncestry( obfMethodEntry.getClassName() ) ) 599 for( String ancestorClassName : m_translationIndex.getAncestry( obfMethodEntry.getClassName() ) )
577 { 600 {
578 MethodEntry ancestorMethodEntry = new MethodEntry( 601 MethodEntry ancestorMethodEntry = new MethodEntry(
579 new ClassEntry( ancestorClassName ), 602 new ClassEntry( ancestorClassName ),
@@ -609,7 +632,7 @@ public class JarIndex
609 MethodEntry interfaceMethodEntry; 632 MethodEntry interfaceMethodEntry;
610 633
611 // is this method on an interface? 634 // is this method on an interface?
612 if( m_ancestries.isInterface( obfMethodEntry.getClassName() ) ) 635 if( isInterface( obfMethodEntry.getClassName() ) )
613 { 636 {
614 interfaceMethodEntry = obfMethodEntry; 637 interfaceMethodEntry = obfMethodEntry;
615 } 638 }
@@ -617,7 +640,7 @@ public class JarIndex
617 { 640 {
618 // get the interface class 641 // get the interface class
619 List<MethodEntry> methodInterfaces = Lists.newArrayList(); 642 List<MethodEntry> methodInterfaces = Lists.newArrayList();
620 for( String interfaceName : m_ancestries.getInterfaces( obfMethodEntry.getClassName() ) ) 643 for( String interfaceName : getInterfaces( obfMethodEntry.getClassName() ) )
621 { 644 {
622 // is this method defined in this interface? 645 // is this method defined in this interface?
623 MethodEntry methodInterface = new MethodEntry( 646 MethodEntry methodInterface = new MethodEntry(
@@ -717,181 +740,44 @@ public class JarIndex
717 return m_anonymousClasses.contains( obfInnerClassName ); 740 return m_anonymousClasses.contains( obfInnerClassName );
718 } 741 }
719 742
720 public MethodEntry getBridgeMethod( MethodEntry methodEntry ) 743 public Set<String> getInterfaces( String className )
721 {
722 return m_bridgeMethods.get( methodEntry );
723 }
724
725 private void renameClasses( Map<String,String> renames )
726 { 744 {
727 // rename class entries 745 Set<String> interfaceNames = new HashSet<String>();
728 Set<ClassEntry> obfClassEntries = Sets.newHashSet(); 746 interfaceNames.addAll( m_interfaces.get( className ) );
729 for( ClassEntry classEntry : m_obfClassEntries ) 747 for( String ancestor : m_translationIndex.getAncestry( className ) )
730 { 748 {
731 if( renames.containsKey( classEntry.getName() ) ) 749 interfaceNames.addAll( m_interfaces.get( ancestor ) );
732 {
733 obfClassEntries.add( new ClassEntry( renames.get( classEntry.getName() ) ) );
734 }
735 else
736 {
737 obfClassEntries.add( classEntry );
738 }
739 } 750 }
740 m_obfClassEntries = obfClassEntries; 751 return interfaceNames;
741
742 // rename others
743 m_ancestries.renameClasses( renames );
744 renameClassesInMultimap( renames, m_methodImplementations );
745 renameClassesInMultimap( renames, m_behaviorReferences );
746 renameClassesInMultimap( renames, m_fieldReferences );
747 } 752 }
748 753
749 private void renameMethods( Map<MethodEntry,MethodEntry> renames ) 754 public Set<String> getImplementingClasses( String targetInterfaceName )
750 { 755 {
751 renameMethodsInMultimap( renames, m_methodImplementations ); 756 // linear search is fast enough for now
752 renameMethodsInMultimap( renames, m_behaviorReferences ); 757 Set<String> classNames = Sets.newHashSet();
753 renameMethodsInMultimap( renames, m_fieldReferences ); 758 for( Map.Entry<String,String> entry : m_interfaces.entries() )
754 }
755
756 private <Key,Val> void renameClassesInMultimap( Map<String,String> renames, Multimap<Key,Val> map )
757 {
758 // for each key/value pair...
759 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
760 for( Map.Entry<Key,Val> entry : map.entries() )
761 { 759 {
762 entriesToAdd.add( new AbstractMap.SimpleEntry<Key,Val>( 760 String className = entry.getKey();
763 renameClassesInThing( renames, entry.getKey() ), 761 String interfaceName = entry.getValue();
764 renameClassesInThing( renames, entry.getValue() ) 762 if( interfaceName.equals( targetInterfaceName ) )
765 ) );
766 }
767 map.clear();
768 for( Map.Entry<Key,Val> entry : entriesToAdd )
769 {
770 map.put( entry.getKey(), entry.getValue() );
771 }
772 }
773
774 @SuppressWarnings( "unchecked" )
775 private <T> T renameClassesInThing( Map<String,String> renames, T thing )
776 {
777 if( thing instanceof String )
778 {
779 String stringEntry = (String)thing;
780 if( renames.containsKey( stringEntry ) )
781 { 763 {
782 return (T)renames.get( stringEntry ); 764 classNames.add( className );
765 m_translationIndex.getSubclassNamesRecursively( classNames, className );
783 } 766 }
784 } 767 }
785 else if( thing instanceof ClassEntry ) 768 return classNames;
786 {
787 ClassEntry classEntry = (ClassEntry)thing;
788 return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) );
789 }
790 else if( thing instanceof FieldEntry )
791 {
792 FieldEntry fieldEntry = (FieldEntry)thing;
793 return (T)new FieldEntry(
794 renameClassesInThing( renames, fieldEntry.getClassEntry() ),
795 fieldEntry.getName()
796 );
797 }
798 else if( thing instanceof ConstructorEntry )
799 {
800 ConstructorEntry constructorEntry = (ConstructorEntry)thing;
801 return (T)new ConstructorEntry(
802 renameClassesInThing( renames, constructorEntry.getClassEntry() ),
803 constructorEntry.getSignature()
804 );
805 }
806 else if( thing instanceof MethodEntry )
807 {
808 MethodEntry methodEntry = (MethodEntry)thing;
809 return (T)new MethodEntry(
810 renameClassesInThing( renames, methodEntry.getClassEntry() ),
811 methodEntry.getName(),
812 methodEntry.getSignature()
813 );
814 }
815 else if( thing instanceof ArgumentEntry )
816 {
817 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
818 return (T)new ArgumentEntry(
819 renameClassesInThing( renames, argumentEntry.getMethodEntry() ),
820 argumentEntry.getIndex(),
821 argumentEntry.getName()
822 );
823 }
824 else if( thing instanceof EntryReference )
825 {
826 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
827 reference.entry = renameClassesInThing( renames, reference.entry );
828 reference.context = renameClassesInThing( renames, reference.context );
829 return thing;
830 }
831 else
832 {
833 throw new Error( "Not an entry: " + thing );
834 }
835
836 return thing;
837 } 769 }
838 770
839 private <Key,Val> void renameMethodsInMultimap( Map<MethodEntry,MethodEntry> renames, Multimap<Key,Val> map ) 771 public boolean isInterface( String className )
840 { 772 {
841 // for each key/value pair... 773 return m_interfaces.containsValue( className );
842 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
843 Iterator<Map.Entry<Key,Val>> iter = map.entries().iterator();
844 while( iter.hasNext() )
845 {
846 Map.Entry<Key,Val> entry = iter.next();
847 iter.remove();
848 entriesToAdd.add( new AbstractMap.SimpleEntry<Key,Val>(
849 renameMethodsInThing( renames, entry.getKey() ),
850 renameMethodsInThing( renames, entry.getValue() )
851 ) );
852 }
853 for( Map.Entry<Key,Val> entry : entriesToAdd )
854 {
855 map.put( entry.getKey(), entry.getValue() );
856 }
857 } 774 }
858 775
859 @SuppressWarnings( "unchecked" ) 776 public MethodEntry getBridgeMethod( MethodEntry methodEntry )
860 private <T> T renameMethodsInThing( Map<MethodEntry,MethodEntry> renames, T thing )
861 { 777 {
862 if( thing instanceof MethodEntry ) 778 return m_bridgeMethods.get( methodEntry );
863 {
864 MethodEntry methodEntry = (MethodEntry)thing;
865 MethodEntry newMethodEntry = renames.get( methodEntry );
866 if( newMethodEntry != null )
867 {
868 return (T)new MethodEntry(
869 methodEntry.getClassEntry(),
870 newMethodEntry.getName(),
871 methodEntry.getSignature()
872 );
873 }
874 return thing;
875 }
876 else if( thing instanceof ArgumentEntry )
877 {
878 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
879 return (T)new ArgumentEntry(
880 renameMethodsInThing( renames, argumentEntry.getMethodEntry() ),
881 argumentEntry.getIndex(),
882 argumentEntry.getName()
883 );
884 }
885 else if( thing instanceof EntryReference )
886 {
887 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
888 reference.entry = renameMethodsInThing( renames, reference.entry );
889 reference.context = renameMethodsInThing( renames, reference.context );
890 return thing;
891 }
892 return thing;
893 } 779 }
894 780
895 public boolean containsObfClass( ClassEntry obfClassEntry ) 781 public boolean containsObfClass( ClassEntry obfClassEntry )
896 { 782 {
897 return m_obfClassEntries.contains( obfClassEntry ); 783 return m_obfClassEntries.contains( obfClassEntry );