summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis
diff options
context:
space:
mode:
authorGravatar jeff2014-09-01 22:52:07 -0400
committerGravatar jeff2014-09-01 22:52:07 -0400
commit360bbd1c2fca8cbd575907b7d930a8072fccb0c2 (patch)
tree93d0f3c4a0901411427df580c5ddb75cf27440f1 /src/cuchaz/enigma/analysis
parentadded copyright notice (diff)
downloadenigma-fork-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.tar.gz
enigma-fork-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.tar.xz
enigma-fork-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.zip
refactored jar,translation index. fixed bug with field renaming when fields are shadowed by subclasses
Diffstat (limited to 'src/cuchaz/enigma/analysis')
-rw-r--r--src/cuchaz/enigma/analysis/Ancestries.java199
-rw-r--r--src/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/DeobfuscatedAncestries.java59
-rw-r--r--src/cuchaz/enigma/analysis/EntryRenamer.java199
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java240
-rw-r--r--src/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java2
-rw-r--r--src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java2
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java126
9 files changed, 394 insertions, 441 deletions
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.Serializable;
14import java.util.AbstractMap;
15import java.util.ArrayList;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Map;
19import java.util.Set;
20
21import javassist.bytecode.Descriptor;
22
23import com.google.common.collect.HashMultimap;
24import com.google.common.collect.Lists;
25import com.google.common.collect.Maps;
26import com.google.common.collect.Multimap;
27import com.google.common.collect.Sets;
28
29public class Ancestries implements Serializable
30{
31 private static final long serialVersionUID = 738687982126844179L;
32
33 private Map<String,String> m_superclasses;
34 private Multimap<String,String> m_interfaces;
35
36 public Ancestries( )
37 {
38 m_superclasses = Maps.newHashMap();
39 m_interfaces = HashMultimap.create();
40 }
41
42 public void addSuperclass( String className, String superclassName )
43 {
44 className = Descriptor.toJvmName( className );
45 superclassName = Descriptor.toJvmName( superclassName );
46
47 if( className.equals( superclassName ) )
48 {
49 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
50 }
51
52 if( !isJre( className ) && !isJre( superclassName ) )
53 {
54 m_superclasses.put( className, superclassName );
55 }
56 }
57
58 public void addInterface( String className, String interfaceName )
59 {
60 className = Descriptor.toJvmName( className );
61 interfaceName = Descriptor.toJvmName( interfaceName );
62
63 if( className.equals( interfaceName ) )
64 {
65 throw new IllegalArgumentException( "Class cannot be its own interface! " + className );
66 }
67
68 if( !isJre( className ) && !isJre( interfaceName ) )
69 {
70 m_interfaces.put( className, interfaceName );
71 }
72 }
73
74 public void renameClasses( Map<String,String> renames )
75 {
76 // rename superclasses
77 Map<String,String> newSuperclasses = Maps.newHashMap();
78 for( Map.Entry<String,String> entry : m_superclasses.entrySet() )
79 {
80 String subclass = renames.get( entry.getKey() );
81 if( subclass == null )
82 {
83 subclass = entry.getKey();
84 }
85 String superclass = renames.get( entry.getValue() );
86 if( superclass == null )
87 {
88 superclass = entry.getValue();
89 }
90 newSuperclasses.put( subclass, superclass );
91 }
92 m_superclasses = newSuperclasses;
93
94 // rename interfaces
95 Set<Map.Entry<String,String>> entriesToAdd = Sets.newHashSet();
96 for( Map.Entry<String,String> entry : m_interfaces.entries() )
97 {
98 String className = renames.get( entry.getKey() );
99 if( className == null )
100 {
101 className = entry.getKey();
102 }
103 String interfaceName = renames.get( entry.getValue() );
104 if( interfaceName == null )
105 {
106 interfaceName = entry.getValue();
107 }
108 entriesToAdd.add( new AbstractMap.SimpleEntry<String,String>( className, interfaceName ) );
109 }
110 m_interfaces.clear();
111 for( Map.Entry<String,String> entry : entriesToAdd )
112 {
113 m_interfaces.put( entry.getKey(), entry.getValue() );
114 }
115 }
116
117 public String getSuperclassName( String className )
118 {
119 return m_superclasses.get( className );
120 }
121
122 public List<String> getAncestry( String className )
123 {
124 List<String> ancestors = new ArrayList<String>();
125 while( className != null )
126 {
127 className = getSuperclassName( className );
128 if( className != null )
129 {
130 ancestors.add( className );
131 }
132 }
133 return ancestors;
134 }
135
136 public Set<String> getInterfaces( String className )
137 {
138 Set<String> interfaceNames = new HashSet<String>();
139 interfaceNames.addAll( m_interfaces.get( className ) );
140 for( String ancestor : getAncestry( className ) )
141 {
142 interfaceNames.addAll( m_interfaces.get( ancestor ) );
143 }
144 return interfaceNames;
145 }
146
147 public List<String> getSubclasses( String className )
148 {
149 // linear search is fast enough for now
150 List<String> subclasses = Lists.newArrayList();
151 for( Map.Entry<String,String> entry : m_superclasses.entrySet() )
152 {
153 String subclass = entry.getKey();
154 String superclass = entry.getValue();
155 if( className.equals( superclass ) )
156 {
157 subclasses.add( subclass );
158 }
159 }
160 return subclasses;
161 }
162
163 public Set<String> getImplementingClasses( String targetInterfaceName )
164 {
165 // linear search is fast enough for now
166 Set<String> classNames = Sets.newHashSet();
167 for( Map.Entry<String,String> entry : m_interfaces.entries() )
168 {
169 String className = entry.getKey();
170 String interfaceName = entry.getValue();
171 if( interfaceName.equals( targetInterfaceName ) )
172 {
173 classNames.add( className );
174 collectSubclasses( classNames, className );
175 }
176 }
177 return classNames;
178 }
179
180 public boolean isInterface( String className )
181 {
182 return m_interfaces.containsValue( className );
183 }
184
185 private void collectSubclasses( Set<String> classNames, String className )
186 {
187 for( String subclassName : getSubclasses( className ) )
188 {
189 classNames.add( subclassName );
190 collectSubclasses( classNames, subclassName );
191 }
192 }
193
194 private boolean isJre( String className )
195 {
196 return className.startsWith( "java/" )
197 || className.startsWith( "javax/" );
198 }
199}
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
54 return className; 54 return className;
55 } 55 }
56 56
57 public void load( Ancestries ancestries ) 57 public void load( JarIndex index )
58 { 58 {
59 // get all method implementations 59 // get all method implementations
60 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); 60 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList();
61 for( String implementingClassName : ancestries.getImplementingClasses( m_entry.getClassName() ) ) 61 for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) )
62 { 62 {
63 nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) ); 63 nodes.add( new ClassImplementationsTreeNode( m_deobfuscatingTranslator, new ClassEntry( implementingClassName ) ) );
64 } 64 }
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
53 return m_obfClassName; 53 return m_obfClassName;
54 } 54 }
55 55
56 public void load( Ancestries ancestries, boolean recurse ) 56 public void load( TranslationIndex ancestries, boolean recurse )
57 { 57 {
58 // get all the child nodes 58 // get all the child nodes
59 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 59 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
60 for( String subclassName : ancestries.getSubclasses( m_obfClassName ) ) 60 for( String subclassName : ancestries.getSubclassNames( m_obfClassName ) )
61 { 61 {
62 nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) ); 62 nodes.add( new ClassInheritanceTreeNode( m_deobfuscatingTranslator, subclassName ) );
63 } 63 }
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.util.Map;
14
15import cuchaz.enigma.mapping.ClassMapping;
16
17public class DeobfuscatedAncestries extends Ancestries
18{
19 private static final long serialVersionUID = 8316248774892618324L;
20
21 private Ancestries m_ancestries;
22 private Map<String,ClassMapping> m_classesByObf;
23 private Map<String,ClassMapping> m_classesByDeobf;
24
25 public DeobfuscatedAncestries( Ancestries ancestries, Map<String,ClassMapping> classesByObf, Map<String,ClassMapping> classesByDeobf )
26 {
27 m_ancestries = ancestries;
28 m_classesByObf = classesByObf;
29 m_classesByDeobf = classesByDeobf;
30 }
31
32 @Override
33 public String getSuperclassName( String deobfClassName )
34 {
35 // obfuscate the class name
36 ClassMapping classIndex = m_classesByDeobf.get( deobfClassName );
37 if( classIndex == null )
38 {
39 return null;
40 }
41 String obfClassName = classIndex.getObfName();
42
43 // get the superclass
44 String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName );
45 if( obfSuperclassName == null )
46 {
47 return null;
48 }
49
50 // deobfuscate the superclass name
51 classIndex = m_classesByObf.get( obfSuperclassName );
52 if( classIndex == null )
53 {
54 return null;
55 }
56
57 return classIndex.getDeobfName();
58 }
59}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.util.AbstractMap;
14import java.util.Iterator;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import com.beust.jcommander.internal.Lists;
20import com.google.common.collect.Multimap;
21import com.google.common.collect.Sets;
22
23import cuchaz.enigma.mapping.ArgumentEntry;
24import cuchaz.enigma.mapping.ClassEntry;
25import cuchaz.enigma.mapping.ConstructorEntry;
26import cuchaz.enigma.mapping.Entry;
27import cuchaz.enigma.mapping.FieldEntry;
28import cuchaz.enigma.mapping.MethodEntry;
29
30public class EntryRenamer
31{
32 public static <T> void renameClassesInSet( Map<String,String> renames, Set<T> set )
33 {
34 List<T> entries = Lists.newArrayList();
35 for( T val : set )
36 {
37 entries.add( renameClassesInThing( renames, val ) );
38 }
39 set.clear();
40 set.addAll( entries );
41 }
42
43 public static <Key,Val> void renameClassesInMap( Map<String,String> renames, Map<Key,Val> map )
44 {
45 // for each key/value pair...
46 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
47 for( Map.Entry<Key,Val> entry : map.entrySet() )
48 {
49 entriesToAdd.add( new AbstractMap.SimpleEntry<Key,Val>(
50 renameClassesInThing( renames, entry.getKey() ),
51 renameClassesInThing( renames, entry.getValue() )
52 ) );
53 }
54 map.clear();
55 for( Map.Entry<Key,Val> entry : entriesToAdd )
56 {
57 map.put( entry.getKey(), entry.getValue() );
58 }
59 }
60
61 public static <Key,Val> void renameClassesInMultimap( Map<String,String> renames, Multimap<Key,Val> map )
62 {
63 // for each key/value pair...
64 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
65 for( Map.Entry<Key,Val> entry : map.entries() )
66 {
67 entriesToAdd.add( new AbstractMap.SimpleEntry<Key,Val>(
68 renameClassesInThing( renames, entry.getKey() ),
69 renameClassesInThing( renames, entry.getValue() )
70 ) );
71 }
72 map.clear();
73 for( Map.Entry<Key,Val> entry : entriesToAdd )
74 {
75 map.put( entry.getKey(), entry.getValue() );
76 }
77 }
78
79 public static <Key,Val> void renameMethodsInMultimap( Map<MethodEntry,MethodEntry> renames, Multimap<Key,Val> map )
80 {
81 // for each key/value pair...
82 Set<Map.Entry<Key,Val>> entriesToAdd = Sets.newHashSet();
83 Iterator<Map.Entry<Key,Val>> iter = map.entries().iterator();
84 while( iter.hasNext() )
85 {
86 Map.Entry<Key,Val> entry = iter.next();
87 iter.remove();
88 entriesToAdd.add( new AbstractMap.SimpleEntry<Key,Val>(
89 renameMethodsInThing( renames, entry.getKey() ),
90 renameMethodsInThing( renames, entry.getValue() )
91 ) );
92 }
93 for( Map.Entry<Key,Val> entry : entriesToAdd )
94 {
95 map.put( entry.getKey(), entry.getValue() );
96 }
97 }
98
99 @SuppressWarnings( "unchecked" )
100 public static <T> T renameMethodsInThing( Map<MethodEntry,MethodEntry> renames, T thing )
101 {
102 if( thing instanceof MethodEntry )
103 {
104 MethodEntry methodEntry = (MethodEntry)thing;
105 MethodEntry newMethodEntry = renames.get( methodEntry );
106 if( newMethodEntry != null )
107 {
108 return (T)new MethodEntry(
109 methodEntry.getClassEntry(),
110 newMethodEntry.getName(),
111 methodEntry.getSignature()
112 );
113 }
114 return thing;
115 }
116 else if( thing instanceof ArgumentEntry )
117 {
118 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
119 return (T)new ArgumentEntry(
120 renameMethodsInThing( renames, argumentEntry.getMethodEntry() ),
121 argumentEntry.getIndex(),
122 argumentEntry.getName()
123 );
124 }
125 else if( thing instanceof EntryReference )
126 {
127 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
128 reference.entry = renameMethodsInThing( renames, reference.entry );
129 reference.context = renameMethodsInThing( renames, reference.context );
130 return thing;
131 }
132 return thing;
133 }
134
135 @SuppressWarnings( "unchecked" )
136 public static <T> T renameClassesInThing( Map<String,String> renames, T thing )
137 {
138 if( thing instanceof String )
139 {
140 String stringEntry = (String)thing;
141 if( renames.containsKey( stringEntry ) )
142 {
143 return (T)renames.get( stringEntry );
144 }
145 }
146 else if( thing instanceof ClassEntry )
147 {
148 ClassEntry classEntry = (ClassEntry)thing;
149 return (T)new ClassEntry( renameClassesInThing( renames, classEntry.getClassName() ) );
150 }
151 else if( thing instanceof FieldEntry )
152 {
153 FieldEntry fieldEntry = (FieldEntry)thing;
154 return (T)new FieldEntry(
155 renameClassesInThing( renames, fieldEntry.getClassEntry() ),
156 fieldEntry.getName()
157 );
158 }
159 else if( thing instanceof ConstructorEntry )
160 {
161 ConstructorEntry constructorEntry = (ConstructorEntry)thing;
162 return (T)new ConstructorEntry(
163 renameClassesInThing( renames, constructorEntry.getClassEntry() ),
164 constructorEntry.getSignature()
165 );
166 }
167 else if( thing instanceof MethodEntry )
168 {
169 MethodEntry methodEntry = (MethodEntry)thing;
170 return (T)new MethodEntry(
171 renameClassesInThing( renames, methodEntry.getClassEntry() ),
172 methodEntry.getName(),
173 methodEntry.getSignature()
174 );
175 }
176 else if( thing instanceof ArgumentEntry )
177 {
178 ArgumentEntry argumentEntry = (ArgumentEntry)thing;
179 return (T)new ArgumentEntry(
180 renameClassesInThing( renames, argumentEntry.getMethodEntry() ),
181 argumentEntry.getIndex(),
182 argumentEntry.getName()
183 );
184 }
185 else if( thing instanceof EntryReference )
186 {
187 EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing;
188 reference.entry = renameClassesInThing( renames, reference.entry );
189 reference.context = renameClassesInThing( renames, reference.context );
190 return thing;
191 }
192 else
193 {
194 throw new Error( "Not an entry: " + thing );
195 }
196
197 return thing;
198 }
199}
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 );
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
74 { 74 {
75 // get all method implementations 75 // get all method implementations
76 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 76 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
77 for( String implementingClassName : index.getAncestries().getImplementingClasses( m_entry.getClassName() ) ) 77 for( String implementingClassName : index.getImplementingClasses( m_entry.getClassName() ) )
78 { 78 {
79 MethodEntry methodEntry = new MethodEntry( 79 MethodEntry methodEntry = new MethodEntry(
80 new ClassEntry( implementingClassName ), 80 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
83 { 83 {
84 // get all the child nodes 84 // get all the child nodes
85 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 85 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
86 for( String subclassName : index.getAncestries().getSubclasses( m_entry.getClassName() ) ) 86 for( String subclassName : index.getTranslationIndex().getSubclassNames( m_entry.getClassName() ) )
87 { 87 {
88 MethodEntry methodEntry = new MethodEntry( 88 MethodEntry methodEntry = new MethodEntry(
89 new ClassEntry( subclassName ), 89 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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import javassist.bytecode.Descriptor;
20
21import com.google.common.collect.HashMultimap;
22import com.google.common.collect.Lists;
23import com.google.common.collect.Maps;
24import com.google.common.collect.Multimap;
25
26public class TranslationIndex implements Serializable
27{
28 private static final long serialVersionUID = 738687982126844179L;
29
30 private Map<String,String> m_superclasses;
31 private Multimap<String,String> m_fields;
32
33 public TranslationIndex( )
34 {
35 m_superclasses = Maps.newHashMap();
36 m_fields = HashMultimap.create();
37 }
38
39 public TranslationIndex( TranslationIndex other )
40 {
41 m_superclasses = Maps.newHashMap( other.m_superclasses );
42 m_fields = HashMultimap.create( other.m_fields );
43 }
44
45 public void addSuperclass( String className, String superclassName )
46 {
47 className = Descriptor.toJvmName( className );
48 superclassName = Descriptor.toJvmName( superclassName );
49
50 if( className.equals( superclassName ) )
51 {
52 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
53 }
54
55 if( !isJre( className ) && !isJre( superclassName ) )
56 {
57 m_superclasses.put( className, superclassName );
58 }
59 }
60
61 public void addField( String className, String fieldName )
62 {
63 m_fields.put( className, fieldName );
64 }
65
66 public void renameClasses( Map<String,String> renames )
67 {
68 EntryRenamer.renameClassesInMap( renames, m_superclasses );
69 EntryRenamer.renameClassesInMultimap( renames, m_fields );
70 }
71
72 public String getSuperclassName( String className )
73 {
74 return m_superclasses.get( className );
75 }
76
77 public List<String> getAncestry( String className )
78 {
79 List<String> ancestors = new ArrayList<String>();
80 while( className != null )
81 {
82 className = getSuperclassName( className );
83 if( className != null )
84 {
85 ancestors.add( className );
86 }
87 }
88 return ancestors;
89 }
90
91 public List<String> getSubclassNames( String className )
92 {
93 // linear search is fast enough for now
94 List<String> subclasses = Lists.newArrayList();
95 for( Map.Entry<String,String> entry : m_superclasses.entrySet() )
96 {
97 String subclass = entry.getKey();
98 String superclass = entry.getValue();
99 if( className.equals( superclass ) )
100 {
101 subclasses.add( subclass );
102 }
103 }
104 return subclasses;
105 }
106
107 public void getSubclassNamesRecursively( Set<String> out, String className )
108 {
109 for( String subclassName : getSubclassNames( className ) )
110 {
111 out.add( subclassName );
112 getSubclassNamesRecursively( out, subclassName );
113 }
114 }
115
116 public boolean containsField( String className, String fieldName )
117 {
118 return m_fields.containsEntry( className, fieldName );
119 }
120
121 private boolean isJre( String className )
122 {
123 return className.startsWith( "java/" )
124 || className.startsWith( "javax/" );
125 }
126}