diff options
Diffstat (limited to 'src/cuchaz/enigma/analysis/JarIndex.java')
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 240 |
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 @@ | |||
| 11 | package cuchaz.enigma.analysis; | 11 | package cuchaz.enigma.analysis; |
| 12 | 12 | ||
| 13 | import java.lang.reflect.Modifier; | 13 | import java.lang.reflect.Modifier; |
| 14 | import java.util.AbstractMap; | ||
| 15 | import java.util.Collection; | 14 | import java.util.Collection; |
| 16 | import java.util.Iterator; | 15 | import java.util.HashSet; |
| 17 | import java.util.List; | 16 | import java.util.List; |
| 18 | import java.util.Map; | 17 | import java.util.Map; |
| 19 | import java.util.Set; | 18 | import java.util.Set; |
| @@ -43,7 +42,6 @@ import com.google.common.collect.Sets; | |||
| 43 | 42 | ||
| 44 | import cuchaz.enigma.Constants; | 43 | import cuchaz.enigma.Constants; |
| 45 | import cuchaz.enigma.bytecode.ClassRenamer; | 44 | import cuchaz.enigma.bytecode.ClassRenamer; |
| 46 | import cuchaz.enigma.mapping.ArgumentEntry; | ||
| 47 | import cuchaz.enigma.mapping.BehaviorEntry; | 45 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 48 | import cuchaz.enigma.mapping.ClassEntry; | 46 | import cuchaz.enigma.mapping.ClassEntry; |
| 49 | import cuchaz.enigma.mapping.ConstructorEntry; | 47 | import cuchaz.enigma.mapping.ConstructorEntry; |
| @@ -55,7 +53,8 @@ import cuchaz.enigma.mapping.Translator; | |||
| 55 | public class JarIndex | 53 | public 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 ); |