summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2014-09-01 22:52:07 -0400
committerGravatar jeff2014-09-01 22:52:07 -0400
commit360bbd1c2fca8cbd575907b7d930a8072fccb0c2 (patch)
tree93d0f3c4a0901411427df580c5ddb75cf27440f1
parentadded copyright notice (diff)
downloadenigma-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.tar.gz
enigma-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.tar.xz
enigma-360bbd1c2fca8cbd575907b7d930a8072fccb0c2.zip
refactored jar,translation index. fixed bug with field renaming when fields are shadowed by subclasses
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java43
-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
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java22
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java (renamed from src/cuchaz/enigma/mapping/Renamer.java)22
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java21
13 files changed, 466 insertions, 477 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 49aa1ff6..0356f923 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -15,10 +15,12 @@ import java.io.FileWriter;
15import java.io.IOException; 15import java.io.IOException;
16import java.io.StringWriter; 16import java.io.StringWriter;
17import java.util.List; 17import java.util.List;
18import java.util.Map;
18import java.util.jar.JarFile; 19import java.util.jar.JarFile;
19 20
20import javassist.bytecode.Descriptor; 21import javassist.bytecode.Descriptor;
21 22
23import com.google.common.collect.Maps;
22import com.strobel.assembler.metadata.MetadataSystem; 24import com.strobel.assembler.metadata.MetadataSystem;
23import com.strobel.assembler.metadata.TypeDefinition; 25import com.strobel.assembler.metadata.TypeDefinition;
24import com.strobel.decompiler.DecompilerContext; 26import com.strobel.decompiler.DecompilerContext;
@@ -42,7 +44,7 @@ import cuchaz.enigma.mapping.FieldEntry;
42import cuchaz.enigma.mapping.Mappings; 44import cuchaz.enigma.mapping.Mappings;
43import cuchaz.enigma.mapping.MethodEntry; 45import cuchaz.enigma.mapping.MethodEntry;
44import cuchaz.enigma.mapping.MethodMapping; 46import cuchaz.enigma.mapping.MethodMapping;
45import cuchaz.enigma.mapping.Renamer; 47import cuchaz.enigma.mapping.MappingsRenamer;
46import cuchaz.enigma.mapping.TranslationDirection; 48import cuchaz.enigma.mapping.TranslationDirection;
47import cuchaz.enigma.mapping.Translator; 49import cuchaz.enigma.mapping.Translator;
48 50
@@ -59,8 +61,8 @@ public class Deobfuscator
59 private DecompilerSettings m_settings; 61 private DecompilerSettings m_settings;
60 private JarIndex m_jarIndex; 62 private JarIndex m_jarIndex;
61 private Mappings m_mappings; 63 private Mappings m_mappings;
62 private Renamer m_renamer; 64 private MappingsRenamer m_renamer;
63 private TranslatingTypeLoader m_typeLoader; 65 private Map<TranslationDirection,Translator> m_translatorCache;
64 66
65 public Deobfuscator( File file ) 67 public Deobfuscator( File file )
66 throws IOException 68 throws IOException
@@ -78,6 +80,9 @@ public class Deobfuscator
78 // DEBUG 80 // DEBUG
79 //m_settings.setShowSyntheticMembers( true ); 81 //m_settings.setShowSyntheticMembers( true );
80 82
83 // init defaults
84 m_translatorCache = Maps.newTreeMap();
85
81 // init mappings 86 // init mappings
82 setMappings( new Mappings() ); 87 setMappings( new Mappings() );
83 } 88 }
@@ -134,21 +139,19 @@ public class Deobfuscator
134 } 139 }
135 140
136 m_mappings = val; 141 m_mappings = val;
137 m_renamer = new Renamer( m_jarIndex, m_mappings ); 142 m_renamer = new MappingsRenamer( m_jarIndex, m_mappings );
138 143 m_translatorCache.clear();
139 // update decompiler options
140 m_typeLoader = new TranslatingTypeLoader(
141 m_jar,
142 m_jarIndex,
143 getTranslator( TranslationDirection.Obfuscating ),
144 getTranslator( TranslationDirection.Deobfuscating )
145 );
146 m_settings.setTypeLoader( m_typeLoader );
147 } 144 }
148 145
149 public Translator getTranslator( TranslationDirection direction ) 146 public Translator getTranslator( TranslationDirection direction )
150 { 147 {
151 return m_mappings.getTranslator( m_jarIndex.getAncestries(), direction ); 148 Translator translator = m_translatorCache.get( direction );
149 if( translator == null )
150 {
151 translator = m_mappings.getTranslator( m_jarIndex.getTranslationIndex(), direction );
152 m_translatorCache.put( direction, translator );
153 }
154 return translator;
152 } 155 }
153 156
154 public void getSeparatedClasses( List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses ) 157 public void getSeparatedClasses( List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses )
@@ -192,6 +195,14 @@ public class Deobfuscator
192 className = classMapping.getDeobfName(); 195 className = classMapping.getDeobfName();
193 } 196 }
194 197
198 // set the type loader
199 m_settings.setTypeLoader( new TranslatingTypeLoader(
200 m_jar,
201 m_jarIndex,
202 getTranslator( TranslationDirection.Obfuscating ),
203 getTranslator( TranslationDirection.Deobfuscating )
204 ) );
205
195 // decompile it! 206 // decompile it!
196 TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( className ).resolve(); 207 TypeDefinition resolvedType = new MetadataSystem( m_settings.getTypeLoader() ).lookupType( className ).resolve();
197 DecompilerContext context = new DecompilerContext(); 208 DecompilerContext context = new DecompilerContext();
@@ -350,8 +361,8 @@ public class Deobfuscator
350 throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); 361 throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() );
351 } 362 }
352 363
353 // clear the type loader cache 364 // clear caches
354 m_typeLoader.clearCache(); 365 m_translatorCache.clear();
355 } 366 }
356 367
357 public boolean hasMapping( Entry obfEntry ) 368 public boolean hasMapping( Entry obfEntry )
diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java
deleted file mode 100644
index 97241084..00000000
--- 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 98648305..4e9dd523 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 2ed141ff..d3fc9dc8 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 b14eca72..00000000
--- 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 00000000..4b2c0b78
--- /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 e7c92bea..a8ac0013 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 b7434e84..8b9dd2d8 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 73f9714c..d77fd858 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 00000000..5311ec70
--- /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}
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java
index 378d4c0a..f52094fd 100644
--- a/src/cuchaz/enigma/mapping/Mappings.java
+++ b/src/cuchaz/enigma/mapping/Mappings.java
@@ -24,8 +24,7 @@ import com.beust.jcommander.internal.Sets;
24import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
25 25
26import cuchaz.enigma.Util; 26import cuchaz.enigma.Util;
27import cuchaz.enigma.analysis.Ancestries; 27import cuchaz.enigma.analysis.TranslationIndex;
28import cuchaz.enigma.analysis.DeobfuscatedAncestries;
29import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 28import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
30 29
31public class Mappings implements Serializable 30public class Mappings implements Serializable
@@ -108,12 +107,27 @@ public class Mappings implements Serializable
108 return m_classesByDeobf.get( deobfName ); 107 return m_classesByDeobf.get( deobfName );
109 } 108 }
110 109
111 public Translator getTranslator( Ancestries ancestries, TranslationDirection direction ) 110 public Translator getTranslator( TranslationIndex index, TranslationDirection direction )
112 { 111 {
112 if( direction == TranslationDirection.Obfuscating )
113 {
114 // deobfuscate the index
115 index = new TranslationIndex( index );
116 Map<String,String> renames = Maps.newHashMap();
117 for( ClassMapping classMapping : classes() )
118 {
119 renames.put( classMapping.getObfName(), classMapping.getDeobfName() );
120 for( ClassMapping innerClassMapping : classMapping.innerClasses() )
121 {
122 renames.put( innerClassMapping.getObfName(), innerClassMapping.getDeobfName() );
123 }
124 }
125 index.renameClasses( renames );
126 }
113 return new Translator( 127 return new Translator(
114 direction, 128 direction,
115 direction.choose( m_classesByObf, m_classesByDeobf ), 129 direction.choose( m_classesByObf, m_classesByDeobf ),
116 direction.choose( ancestries, new DeobfuscatedAncestries( ancestries, m_classesByObf, m_classesByDeobf ) ) 130 index
117 ); 131 );
118 } 132 }
119 133
diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java
index 15d9af4d..9d036d8f 100644
--- a/src/cuchaz/enigma/mapping/Renamer.java
+++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -18,12 +18,12 @@ import java.util.zip.GZIPOutputStream;
18 18
19import cuchaz.enigma.analysis.JarIndex; 19import cuchaz.enigma.analysis.JarIndex;
20 20
21public class Renamer 21public class MappingsRenamer
22{ 22{
23 private JarIndex m_index; 23 private JarIndex m_index;
24 private Mappings m_mappings; 24 private Mappings m_mappings;
25 25
26 public Renamer( JarIndex index, Mappings mappings ) 26 public MappingsRenamer( JarIndex index, Mappings mappings )
27 { 27 {
28 m_index = index; 28 m_index = index;
29 m_mappings = mappings; 29 m_mappings = mappings;
@@ -77,8 +77,8 @@ public class Renamer
77 MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() ); 77 MethodEntry targetEntry = new MethodEntry( entry.getClassEntry(), deobfName, entry.getSignature() );
78 if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) 78 if( m_mappings.containsDeobfMethod( entry.getClassEntry(), deobfName, entry.getSignature() ) || m_index.containsObfMethod( targetEntry ) )
79 { 79 {
80 String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() ); 80 String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( entry.getClassName() );
81 throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); 81 throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName );
82 } 82 }
83 } 83 }
84 84
@@ -94,12 +94,12 @@ public class Renamer
94 MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() ); 94 MethodEntry targetEntry = new MethodEntry( obf.getClassEntry(), deobfName, obf.getSignature() );
95 if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) ) 95 if( m_mappings.containsDeobfMethod( obf.getClassEntry(), deobfName, obf.getSignature() ) || m_index.containsObfMethod( targetEntry ) )
96 { 96 {
97 String className = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() ); 97 String deobfClassName = getTranslator( TranslationDirection.Deobfuscating ).translateClass( obf.getClassName() );
98 throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + className ); 98 throw new IllegalNameException( deobfName, "There is already a method with that name and signature in class " + deobfClassName );
99 } 99 }
100 100
101 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() ); 101 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() );
102 String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); 102 String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() );
103 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); 103 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature );
104 } 104 }
105 105
@@ -151,10 +151,14 @@ public class Renamer
151 151
152 private void updateDeobfMethodSignatures( ) 152 private void updateDeobfMethodSignatures( )
153 { 153 {
154 Translator translator = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating );
155 for( ClassMapping classMapping : m_mappings.m_classesByObf.values() ) 154 for( ClassMapping classMapping : m_mappings.m_classesByObf.values() )
156 { 155 {
157 classMapping.updateDeobfMethodSignatures( translator ); 156 classMapping.updateDeobfMethodSignatures( getTranslator( TranslationDirection.Deobfuscating ) );
158 } 157 }
159 } 158 }
159
160 private Translator getTranslator( TranslationDirection direction )
161 {
162 return m_mappings.getTranslator( m_index.getTranslationIndex(), direction );
163 }
160} 164}
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
index 23bf0951..f5aafdd1 100644
--- a/src/cuchaz/enigma/mapping/Translator.java
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -16,27 +16,27 @@ import java.util.Map;
16 16
17import com.beust.jcommander.internal.Maps; 17import com.beust.jcommander.internal.Maps;
18 18
19import cuchaz.enigma.analysis.Ancestries; 19import cuchaz.enigma.analysis.TranslationIndex;
20import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 20import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
21 21
22public class Translator 22public class Translator
23{ 23{
24 private TranslationDirection m_direction; 24 private TranslationDirection m_direction;
25 public Map<String,ClassMapping> m_classes; 25 private Map<String,ClassMapping> m_classes;
26 private Ancestries m_ancestries; 26 private TranslationIndex m_index;
27 27
28 public Translator( ) 28 public Translator( )
29 { 29 {
30 m_direction = null; 30 m_direction = null;
31 m_classes = Maps.newHashMap(); 31 m_classes = Maps.newHashMap();
32 m_ancestries = new Ancestries(); 32 m_index = new TranslationIndex();
33 } 33 }
34 34
35 protected Translator( TranslationDirection direction, Map<String,ClassMapping> classes, Ancestries ancestries ) 35 public Translator( TranslationDirection direction, Map<String,ClassMapping> classes, TranslationIndex index )
36 { 36 {
37 m_direction = direction; 37 m_direction = direction;
38 m_classes = classes; 38 m_classes = classes;
39 m_ancestries = ancestries; 39 m_index = index;
40 } 40 }
41 41
42 @SuppressWarnings( "unchecked" ) 42 @SuppressWarnings( "unchecked" )
@@ -145,6 +145,13 @@ public class Translator
145 return translatedName; 145 return translatedName;
146 } 146 }
147 } 147 }
148
149 // is the field implemented in this class?
150 if( m_index.containsField( className, in.getName() ) )
151 {
152 // stop traversing the superclass chain
153 break;
154 }
148 } 155 }
149 return null; 156 return null;
150 } 157 }
@@ -291,7 +298,7 @@ public class Translator
291 { 298 {
292 List<String> ancestry = new ArrayList<String>(); 299 List<String> ancestry = new ArrayList<String>();
293 ancestry.add( className ); 300 ancestry.add( className );
294 ancestry.addAll( m_ancestries.getAncestry( className ) ); 301 ancestry.addAll( m_index.getAncestry( className ) );
295 return ancestry; 302 return ancestry;
296 } 303 }
297 304