From 360bbd1c2fca8cbd575907b7d930a8072fccb0c2 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 1 Sep 2014 22:52:07 -0400 Subject: refactored jar,translation index. fixed bug with field renaming when fields are shadowed by subclasses --- src/cuchaz/enigma/analysis/Ancestries.java | 199 ----------------- .../analysis/ClassImplementationsTreeNode.java | 4 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 4 +- .../enigma/analysis/DeobfuscatedAncestries.java | 59 ----- src/cuchaz/enigma/analysis/EntryRenamer.java | 199 +++++++++++++++++ src/cuchaz/enigma/analysis/JarIndex.java | 240 ++++++--------------- .../analysis/MethodImplementationsTreeNode.java | 2 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 2 +- src/cuchaz/enigma/analysis/TranslationIndex.java | 126 +++++++++++ 9 files changed, 394 insertions(+), 441 deletions(-) delete mode 100644 src/cuchaz/enigma/analysis/Ancestries.java delete mode 100644 src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java create mode 100644 src/cuchaz/enigma/analysis/EntryRenamer.java create mode 100644 src/cuchaz/enigma/analysis/TranslationIndex.java (limited to 'src/cuchaz/enigma/analysis') diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java deleted file mode 100644 index 9724108..0000000 --- a/src/cuchaz/enigma/analysis/Ancestries.java +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javassist.bytecode.Descriptor; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -public class Ancestries implements Serializable -{ - private static final long serialVersionUID = 738687982126844179L; - - private Map m_superclasses; - private Multimap m_interfaces; - - public Ancestries( ) - { - m_superclasses = Maps.newHashMap(); - m_interfaces = HashMultimap.create(); - } - - public void addSuperclass( String className, String superclassName ) - { - className = Descriptor.toJvmName( className ); - superclassName = Descriptor.toJvmName( superclassName ); - - if( className.equals( superclassName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); - } - - if( !isJre( className ) && !isJre( superclassName ) ) - { - m_superclasses.put( className, superclassName ); - } - } - - public void addInterface( String className, String interfaceName ) - { - className = Descriptor.toJvmName( className ); - interfaceName = Descriptor.toJvmName( interfaceName ); - - if( className.equals( interfaceName ) ) - { - throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); - } - - if( !isJre( className ) && !isJre( interfaceName ) ) - { - m_interfaces.put( className, interfaceName ); - } - } - - public void renameClasses( Map renames ) - { - // rename superclasses - Map newSuperclasses = Maps.newHashMap(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { - String subclass = renames.get( entry.getKey() ); - if( subclass == null ) - { - subclass = entry.getKey(); - } - String superclass = renames.get( entry.getValue() ); - if( superclass == null ) - { - superclass = entry.getValue(); - } - newSuperclasses.put( subclass, superclass ); - } - m_superclasses = newSuperclasses; - - // rename interfaces - Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : m_interfaces.entries() ) - { - String className = renames.get( entry.getKey() ); - if( className == null ) - { - className = entry.getKey(); - } - String interfaceName = renames.get( entry.getValue() ); - if( interfaceName == null ) - { - interfaceName = entry.getValue(); - } - entriesToAdd.add( new AbstractMap.SimpleEntry( className, interfaceName ) ); - } - m_interfaces.clear(); - for( Map.Entry entry : entriesToAdd ) - { - m_interfaces.put( entry.getKey(), entry.getValue() ); - } - } - - public String getSuperclassName( String className ) - { - return m_superclasses.get( className ); - } - - public List getAncestry( String className ) - { - List ancestors = new ArrayList(); - while( className != null ) - { - className = getSuperclassName( className ); - if( className != null ) - { - ancestors.add( className ); - } - } - return ancestors; - } - - public Set getInterfaces( String className ) - { - Set interfaceNames = new HashSet(); - interfaceNames.addAll( m_interfaces.get( className ) ); - for( String ancestor : getAncestry( className ) ) - { - interfaceNames.addAll( m_interfaces.get( ancestor ) ); - } - return interfaceNames; - } - - public List getSubclasses( String className ) - { - // linear search is fast enough for now - List subclasses = Lists.newArrayList(); - for( Map.Entry entry : m_superclasses.entrySet() ) - { - String subclass = entry.getKey(); - String superclass = entry.getValue(); - if( className.equals( superclass ) ) - { - subclasses.add( subclass ); - } - } - return subclasses; - } - - public Set getImplementingClasses( String targetInterfaceName ) - { - // linear search is fast enough for now - Set classNames = Sets.newHashSet(); - for( Map.Entry entry : m_interfaces.entries() ) - { - String className = entry.getKey(); - String interfaceName = entry.getValue(); - if( interfaceName.equals( targetInterfaceName ) ) - { - classNames.add( className ); - collectSubclasses( classNames, className ); - } - } - return classNames; - } - - public boolean isInterface( String className ) - { - return m_interfaces.containsValue( className ); - } - - private void collectSubclasses( Set classNames, String className ) - { - for( String subclassName : getSubclasses( className ) ) - { - classNames.add( subclassName ); - collectSubclasses( classNames, subclassName ); - } - } - - private boolean isJre( String className ) - { - return className.startsWith( "java/" ) - || className.startsWith( "javax/" ); - } -} diff --git a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 9864830..4e9dd52 100644 --- a/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -54,11 +54,11 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode return className; } - public void load( Ancestries ancestries ) + public void load( JarIndex index ) { // get all method implementations List nodes = Lists.newArrayList(); - for( String implementingClassName : ancestries.getImplementingClasses( m_entry.getClassName() ) ) + for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) { nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) ); } diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 2ed141f..d3fc9dc 100644 --- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -53,11 +53,11 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode return m_obfClassName; } - public void load( Ancestries ancestries, boolean recurse ) + public void load( TranslationIndex ancestries, boolean recurse ) { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) + for( String subclassName : ancestries.getSubclassNames( m_obfClassName ) ) { nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); } diff --git a/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java b/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java deleted file mode 100644 index b14eca7..0000000 --- a/src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.analysis; - -import java.util.Map; - -import cuchaz.enigma.mapping.ClassMapping; - -public class DeobfuscatedAncestries extends Ancestries -{ - private static final long serialVersionUID = 8316248774892618324L; - - private Ancestries m_ancestries; - private Map m_classesByObf; - private Map m_classesByDeobf; - - public DeobfuscatedAncestries( Ancestries ancestries, Map classesByObf, Map classesByDeobf ) - { - m_ancestries = ancestries; - m_classesByObf = classesByObf; - m_classesByDeobf = classesByDeobf; - } - - @Override - public String getSuperclassName( String deobfClassName ) - { - // obfuscate the class name - ClassMapping classIndex = m_classesByDeobf.get( deobfClassName ); - if( classIndex == null ) - { - return null; - } - String obfClassName = classIndex.getObfName(); - - // get the superclass - String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName ); - if( obfSuperclassName == null ) - { - return null; - } - - // deobfuscate the superclass name - classIndex = m_classesByObf.get( obfSuperclassName ); - if( classIndex == null ) - { - return null; - } - - return classIndex.getDeobfName(); - } -} diff --git a/src/cuchaz/enigma/analysis/EntryRenamer.java b/src/cuchaz/enigma/analysis/EntryRenamer.java new file mode 100644 index 0000000..4b2c0b7 --- /dev/null +++ b/src/cuchaz/enigma/analysis/EntryRenamer.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.util.AbstractMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryRenamer +{ + public static void renameClassesInSet( Map renames, Set set ) + { + List entries = Lists.newArrayList(); + for( T val : set ) + { + entries.add( renameClassesInThing( renames, val ) ); + } + set.clear(); + set.addAll( entries ); + } + + public static void renameClassesInMap( Map renames, Map map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : map.entrySet() ) + { + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameClassesInThing( renames, entry.getKey() ), + renameClassesInThing( renames, entry.getValue() ) + ) ); + } + map.clear(); + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + public static void renameClassesInMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + for( Map.Entry entry : map.entries() ) + { + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameClassesInThing( renames, entry.getKey() ), + renameClassesInThing( renames, entry.getValue() ) + ) ); + } + map.clear(); + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + public static void renameMethodsInMultimap( Map renames, Multimap map ) + { + // for each key/value pair... + Set> entriesToAdd = Sets.newHashSet(); + Iterator> iter = map.entries().iterator(); + while( iter.hasNext() ) + { + Map.Entry entry = iter.next(); + iter.remove(); + entriesToAdd.add( new AbstractMap.SimpleEntry( + renameMethodsInThing( renames, entry.getKey() ), + renameMethodsInThing( renames, entry.getValue() ) + ) ); + } + for( Map.Entry entry : entriesToAdd ) + { + map.put( entry.getKey(), entry.getValue() ); + } + } + + @SuppressWarnings( "unchecked" ) + public static T renameMethodsInThing( Map renames, T thing ) + { + if( thing instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)thing; + MethodEntry newMethodEntry = renames.get( methodEntry ); + if( newMethodEntry != null ) + { + return (T)new MethodEntry( + methodEntry.getClassEntry(), + newMethodEntry.getName(), + methodEntry.getSignature() + ); + } + return thing; + } + else if( thing instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameMethodsInThing( renames, reference.entry ); + reference.context = renameMethodsInThing( renames, reference.context ); + return thing; + } + return thing; + } + + @SuppressWarnings( "unchecked" ) + public static T renameClassesInThing( Map renames, T thing ) + { + if( thing instanceof String ) + { + String stringEntry = (String)thing; + if( renames.containsKey( stringEntry ) ) + { + return (T)renames.get( stringEntry ); + } + } + else if( thing instanceof ClassEntry ) + { + ClassEntry classEntry = (ClassEntry)thing; + return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); + } + else if( thing instanceof FieldEntry ) + { + FieldEntry fieldEntry = (FieldEntry)thing; + return (T)new FieldEntry( + renameClassesInThing( renames, fieldEntry.getClassEntry() ), + fieldEntry.getName() + ); + } + else if( thing instanceof ConstructorEntry ) + { + ConstructorEntry constructorEntry = (ConstructorEntry)thing; + return (T)new ConstructorEntry( + renameClassesInThing( renames, constructorEntry.getClassEntry() ), + constructorEntry.getSignature() + ); + } + else if( thing instanceof MethodEntry ) + { + MethodEntry methodEntry = (MethodEntry)thing; + return (T)new MethodEntry( + renameClassesInThing( renames, methodEntry.getClassEntry() ), + methodEntry.getName(), + methodEntry.getSignature() + ); + } + else if( thing instanceof ArgumentEntry ) + { + ArgumentEntry argumentEntry = (ArgumentEntry)thing; + return (T)new ArgumentEntry( + renameClassesInThing( renames, argumentEntry.getMethodEntry() ), + argumentEntry.getIndex(), + argumentEntry.getName() + ); + } + else if( thing instanceof EntryReference ) + { + EntryReference reference = (EntryReference)thing; + reference.entry = renameClassesInThing( renames, reference.entry ); + reference.context = renameClassesInThing( renames, reference.context ); + return thing; + } + else + { + throw new Error( "Not an entry: " + thing ); + } + + return thing; + } +} 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 @@ package cuchaz.enigma.analysis; import java.lang.reflect.Modifier; -import java.util.AbstractMap; import java.util.Collection; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,7 +42,6 @@ import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.bytecode.ClassRenamer; -import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ConstructorEntry; @@ -55,7 +53,8 @@ import cuchaz.enigma.mapping.Translator; public class JarIndex { private Set m_obfClassEntries; - private Ancestries m_ancestries; + private TranslationIndex m_translationIndex; + private Multimap m_interfaces; private Map m_access; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; @@ -68,7 +67,8 @@ public class JarIndex public JarIndex( ) { m_obfClassEntries = Sets.newHashSet(); - m_ancestries = new Ancestries(); + m_translationIndex = new TranslationIndex(); + m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); @@ -109,15 +109,25 @@ public class JarIndex } } - // step 3: index the types, methods + // step 3: index extends, implements, fields, and methods for( CtClass c : JarClassIterator.classes( jar ) ) { ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage ); String className = Descriptor.toJvmName( c.getName() ); - m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); + m_translationIndex.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); for( String interfaceName : c.getClassFile().getInterfaces() ) { - m_ancestries.addInterface( className, Descriptor.toJvmName( interfaceName ) ); + className = Descriptor.toJvmName( className ); + interfaceName = Descriptor.toJvmName( interfaceName ); + if( className.equals( interfaceName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own interface! " + className ); + } + m_interfaces.put( className, interfaceName ); + } + for( CtField field : c.getDeclaredFields() ) + { + indexField( field ); } for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -159,11 +169,24 @@ public class JarIndex { renames.put( entry.getKey(), entry.getValue() + "$" + new ClassEntry( entry.getKey() ).getSimpleName() ); } - renameClasses( renames ); + EntryRenamer.renameClassesInSet( renames, m_obfClassEntries ); + m_translationIndex.renameClasses( renames ); + EntryRenamer.renameClassesInMultimap( renames, m_interfaces ); + EntryRenamer.renameClassesInMultimap( renames, m_methodImplementations ); + EntryRenamer.renameClassesInMultimap( renames, m_behaviorReferences ); + EntryRenamer.renameClassesInMultimap( renames, m_fieldReferences ); } - // step 5: update other indices with bridge method info - renameMethods( m_bridgeMethods ); + // step 6: update other indices with bridge method info + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_methodImplementations ); + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_behaviorReferences ); + EntryRenamer.renameMethodsInMultimap( m_bridgeMethods, m_fieldReferences ); + } + + private void indexField( CtField field ) + { + String className = Descriptor.toJvmName( field.getDeclaringClass().getName() ); + m_translationIndex.addField( className, field.getName() ); } private void indexBehavior( CtBehavior behavior ) @@ -528,9 +551,9 @@ public class JarIndex return m_obfClassEntries; } - public Ancestries getAncestries( ) + public TranslationIndex getTranslationIndex( ) { - return m_ancestries; + return m_translationIndex; } public Access getAccess( Entry entry ) @@ -553,11 +576,11 @@ public class JarIndex // get the root node List ancestry = Lists.newArrayList(); ancestry.add( obfClassEntry.getName() ); - ancestry.addAll( m_ancestries.getAncestry( obfClassEntry.getName() ) ); + ancestry.addAll( m_translationIndex.getAncestry( obfClassEntry.getName() ) ); ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( deobfuscatingTranslator, ancestry.get( ancestry.size() - 1 ) ); // expand all children recursively - rootNode.load( m_ancestries, true ); + rootNode.load( m_translationIndex, true ); return rootNode; } @@ -565,7 +588,7 @@ public class JarIndex public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); - node.load( m_ancestries ); + node.load( this ); return node; } @@ -573,7 +596,7 @@ public class JarIndex { // travel to the ancestor implementation String baseImplementationClassName = obfMethodEntry.getClassName(); - for( String ancestorClassName : m_ancestries.getAncestry( obfMethodEntry.getClassName() ) ) + for( String ancestorClassName : m_translationIndex.getAncestry( obfMethodEntry.getClassName() ) ) { MethodEntry ancestorMethodEntry = new MethodEntry( new ClassEntry( ancestorClassName ), @@ -609,7 +632,7 @@ public class JarIndex MethodEntry interfaceMethodEntry; // is this method on an interface? - if( m_ancestries.isInterface( obfMethodEntry.getClassName() ) ) + if( isInterface( obfMethodEntry.getClassName() ) ) { interfaceMethodEntry = obfMethodEntry; } @@ -617,7 +640,7 @@ public class JarIndex { // get the interface class List methodInterfaces = Lists.newArrayList(); - for( String interfaceName : m_ancestries.getInterfaces( obfMethodEntry.getClassName() ) ) + for( String interfaceName : getInterfaces( obfMethodEntry.getClassName() ) ) { // is this method defined in this interface? MethodEntry methodInterface = new MethodEntry( @@ -717,181 +740,44 @@ public class JarIndex return m_anonymousClasses.contains( obfInnerClassName ); } - public MethodEntry getBridgeMethod( MethodEntry methodEntry ) - { - return m_bridgeMethods.get( methodEntry ); - } - - private void renameClasses( Map renames ) + public Set getInterfaces( String className ) { - // rename class entries - Set obfClassEntries = Sets.newHashSet(); - for( ClassEntry classEntry : m_obfClassEntries ) + Set interfaceNames = new HashSet(); + interfaceNames.addAll( m_interfaces.get( className ) ); + for( String ancestor : m_translationIndex.getAncestry( className ) ) { - if( renames.containsKey( classEntry.getName() ) ) - { - obfClassEntries.add( new ClassEntry( renames.get( classEntry.getName() ) ) ); - } - else - { - obfClassEntries.add( classEntry ); - } + interfaceNames.addAll( m_interfaces.get( ancestor ) ); } - m_obfClassEntries = obfClassEntries; - - // rename others - m_ancestries.renameClasses( renames ); - renameClassesInMultimap( renames, m_methodImplementations ); - renameClassesInMultimap( renames, m_behaviorReferences ); - renameClassesInMultimap( renames, m_fieldReferences ); + return interfaceNames; } - private void renameMethods( Map renames ) + public Set getImplementingClasses( String targetInterfaceName ) { - renameMethodsInMultimap( renames, m_methodImplementations ); - renameMethodsInMultimap( renames, m_behaviorReferences ); - renameMethodsInMultimap( renames, m_fieldReferences ); - } - - private void renameClassesInMultimap( Map renames, Multimap map ) - { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - for( Map.Entry entry : map.entries() ) + // linear search is fast enough for now + Set classNames = Sets.newHashSet(); + for( Map.Entry entry : m_interfaces.entries() ) { - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameClassesInThing( renames, entry.getKey() ), - renameClassesInThing( renames, entry.getValue() ) - ) ); - } - map.clear(); - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); - } - } - - @SuppressWarnings( "unchecked" ) - private T renameClassesInThing( Map renames, T thing ) - { - if( thing instanceof String ) - { - String stringEntry = (String)thing; - if( renames.containsKey( stringEntry ) ) + String className = entry.getKey(); + String interfaceName = entry.getValue(); + if( interfaceName.equals( targetInterfaceName ) ) { - return (T)renames.get( stringEntry ); + classNames.add( className ); + m_translationIndex.getSubclassNamesRecursively( classNames, className ); } } - else if( thing instanceof ClassEntry ) - { - ClassEntry classEntry = (ClassEntry)thing; - return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) ); - } - else if( thing instanceof FieldEntry ) - { - FieldEntry fieldEntry = (FieldEntry)thing; - return (T)new FieldEntry( - renameClassesInThing( renames, fieldEntry.getClassEntry() ), - fieldEntry.getName() - ); - } - else if( thing instanceof ConstructorEntry ) - { - ConstructorEntry constructorEntry = (ConstructorEntry)thing; - return (T)new ConstructorEntry( - renameClassesInThing( renames, constructorEntry.getClassEntry() ), - constructorEntry.getSignature() - ); - } - else if( thing instanceof MethodEntry ) - { - MethodEntry methodEntry = (MethodEntry)thing; - return (T)new MethodEntry( - renameClassesInThing( renames, methodEntry.getClassEntry() ), - methodEntry.getName(), - methodEntry.getSignature() - ); - } - else if( thing instanceof ArgumentEntry ) - { - ArgumentEntry argumentEntry = (ArgumentEntry)thing; - return (T)new ArgumentEntry( - renameClassesInThing( renames, argumentEntry.getMethodEntry() ), - argumentEntry.getIndex(), - argumentEntry.getName() - ); - } - else if( thing instanceof EntryReference ) - { - EntryReference reference = (EntryReference)thing; - reference.entry = renameClassesInThing( renames, reference.entry ); - reference.context = renameClassesInThing( renames, reference.context ); - return thing; - } - else - { - throw new Error( "Not an entry: " + thing ); - } - - return thing; + return classNames; } - private void renameMethodsInMultimap( Map renames, Multimap map ) + public boolean isInterface( String className ) { - // for each key/value pair... - Set> entriesToAdd = Sets.newHashSet(); - Iterator> iter = map.entries().iterator(); - while( iter.hasNext() ) - { - Map.Entry entry = iter.next(); - iter.remove(); - entriesToAdd.add( new AbstractMap.SimpleEntry( - renameMethodsInThing( renames, entry.getKey() ), - renameMethodsInThing( renames, entry.getValue() ) - ) ); - } - for( Map.Entry entry : entriesToAdd ) - { - map.put( entry.getKey(), entry.getValue() ); - } + return m_interfaces.containsValue( className ); } - @SuppressWarnings( "unchecked" ) - private T renameMethodsInThing( Map renames, T thing ) + public MethodEntry getBridgeMethod( MethodEntry methodEntry ) { - if( thing instanceof MethodEntry ) - { - MethodEntry methodEntry = (MethodEntry)thing; - MethodEntry newMethodEntry = renames.get( methodEntry ); - if( newMethodEntry != null ) - { - return (T)new MethodEntry( - methodEntry.getClassEntry(), - newMethodEntry.getName(), - methodEntry.getSignature() - ); - } - return thing; - } - else if( thing instanceof ArgumentEntry ) - { - ArgumentEntry argumentEntry = (ArgumentEntry)thing; - return (T)new ArgumentEntry( - renameMethodsInThing( renames, argumentEntry.getMethodEntry() ), - argumentEntry.getIndex(), - argumentEntry.getName() - ); - } - else if( thing instanceof EntryReference ) - { - EntryReference reference = (EntryReference)thing; - reference.entry = renameMethodsInThing( renames, reference.entry ); - reference.context = renameMethodsInThing( renames, reference.context ); - return thing; - } - return thing; + return m_bridgeMethods.get( methodEntry ); } - + public boolean containsObfClass( ClassEntry obfClassEntry ) { return m_obfClassEntries.contains( obfClassEntry ); diff --git a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index b7434e8..8b9dd2d 100644 --- a/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -74,7 +74,7 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { // get all method implementations List nodes = Lists.newArrayList(); - for( String implementingClassName : index.getAncestries().getImplementingClasses( m_entry.getClassName() ) ) + for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) ) { MethodEntry methodEntry = new MethodEntry( new ClassEntry( implementingClassName ), diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 73f9714..d77fd85 100644 --- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -83,7 +83,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { // get all the child nodes List nodes = Lists.newArrayList(); - for( String subclassName : index.getAncestries().getSubclasses( m_entry.getClassName() ) ) + for( String subclassName : index.getTranslationIndex().getSubclassNames( m_entry.getClassName() ) ) { MethodEntry methodEntry = new MethodEntry( new ClassEntry( subclassName ), diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java new file mode 100644 index 0000000..5311ec7 --- /dev/null +++ b/src/cuchaz/enigma/analysis/TranslationIndex.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.analysis; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javassist.bytecode.Descriptor; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +public class TranslationIndex implements Serializable +{ + private static final long serialVersionUID = 738687982126844179L; + + private Map m_superclasses; + private Multimap m_fields; + + public TranslationIndex( ) + { + m_superclasses = Maps.newHashMap(); + m_fields = HashMultimap.create(); + } + + public TranslationIndex( TranslationIndex other ) + { + m_superclasses = Maps.newHashMap( other.m_superclasses ); + m_fields = HashMultimap.create( other.m_fields ); + } + + public void addSuperclass( String className, String superclassName ) + { + className = Descriptor.toJvmName( className ); + superclassName = Descriptor.toJvmName( superclassName ); + + if( className.equals( superclassName ) ) + { + throw new IllegalArgumentException( "Class cannot be its own superclass! " + className ); + } + + if( !isJre( className ) && !isJre( superclassName ) ) + { + m_superclasses.put( className, superclassName ); + } + } + + public void addField( String className, String fieldName ) + { + m_fields.put( className, fieldName ); + } + + public void renameClasses( Map renames ) + { + EntryRenamer.renameClassesInMap( renames, m_superclasses ); + EntryRenamer.renameClassesInMultimap( renames, m_fields ); + } + + public String getSuperclassName( String className ) + { + return m_superclasses.get( className ); + } + + public List getAncestry( String className ) + { + List ancestors = new ArrayList(); + while( className != null ) + { + className = getSuperclassName( className ); + if( className != null ) + { + ancestors.add( className ); + } + } + return ancestors; + } + + public List getSubclassNames( String className ) + { + // linear search is fast enough for now + List subclasses = Lists.newArrayList(); + for( Map.Entry entry : m_superclasses.entrySet() ) + { + String subclass = entry.getKey(); + String superclass = entry.getValue(); + if( className.equals( superclass ) ) + { + subclasses.add( subclass ); + } + } + return subclasses; + } + + public void getSubclassNamesRecursively( Set out, String className ) + { + for( String subclassName : getSubclassNames( className ) ) + { + out.add( subclassName ); + getSubclassNamesRecursively( out, subclassName ); + } + } + + public boolean containsField( String className, String fieldName ) + { + return m_fields.containsEntry( className, fieldName ); + } + + private boolean isJre( String className ) + { + return className.startsWith( "java/" ) + || className.startsWith( "javax/" ); + } +} -- cgit v1.2.3