summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz')
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java2
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java225
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java143
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java11
-rw-r--r--src/main/java/cuchaz/enigma/analysis/BridgeMarker.java38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java47
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java26
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java23
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java50
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java123
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java588
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java9
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java12
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java (renamed from src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java)38
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java72
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java36
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java)83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java123
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/AccessFlags.java79
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java54
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java58
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java539
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java264
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InfoType.java266
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java87
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java56
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java124
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java57
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java75
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java56
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java29
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java161
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java144
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java142
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java62
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java106
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java82
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java66
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java18
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java4
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java110
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java (renamed from src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java)17
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassEntry.java26
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java163
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java105
-rw-r--r--src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java319
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Entry.java4
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java101
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java34
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java51
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java75
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java65
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java (renamed from src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java)12
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java45
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java18
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java163
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java4
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java35
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java113
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java59
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java130
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java50
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java106
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java344
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java240
82 files changed, 2372 insertions, 4649 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index f546eb1..59eb1b6 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -99,7 +99,7 @@ public class CommandMain {
99 private static void convertMappings(String[] args) throws Exception { 99 private static void convertMappings(String[] args) throws Exception {
100 File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true)); 100 File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true));
101 File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); 101 File result = getWritableFile(getArg(args, 2, "enigma mapping", true));
102 String name = getArg(args, 3, "format type", true); 102 String name = getArg(args, 3, "format desc", true);
103 Mappings.FormatType formatType; 103 Mappings.FormatType formatType;
104 try { 104 try {
105 formatType = Mappings.FormatType.valueOf(name.toUpperCase()); 105 formatType = Mappings.FormatType.valueOf(name.toUpperCase());
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 1e99af2..e1454c7 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -31,50 +31,55 @@ import cuchaz.enigma.bytecode.ClassPublifier;
31import cuchaz.enigma.mapping.*; 31import cuchaz.enigma.mapping.*;
32import cuchaz.enigma.throwables.IllegalNameException; 32import cuchaz.enigma.throwables.IllegalNameException;
33import cuchaz.enigma.utils.Utils; 33import cuchaz.enigma.utils.Utils;
34import javassist.CtClass; 34import org.objectweb.asm.ClassWriter;
35import javassist.bytecode.Descriptor; 35import org.objectweb.asm.Opcodes;
36import org.objectweb.asm.tree.ClassNode;
36 37
37import java.io.*; 38import java.io.*;
38import java.util.*; 39import java.util.*;
40import java.util.concurrent.atomic.AtomicInteger;
39import java.util.jar.JarEntry; 41import java.util.jar.JarEntry;
40import java.util.jar.JarFile; 42import java.util.jar.JarFile;
41import java.util.jar.JarOutputStream; 43import java.util.jar.JarOutputStream;
42 44
43public class Deobfuscator { 45public class Deobfuscator {
44 46
47 private final ReferencedEntryPool entryPool = new ReferencedEntryPool();
45 private final JarFile jar; 48 private final JarFile jar;
49 private final ParsedJar parsedJar;
46 private final DecompilerSettings settings; 50 private final DecompilerSettings settings;
47 private final JarIndex jarIndex; 51 private final JarIndex jarIndex;
48 private final MappingsRenamer renamer; 52 private final MappingsRenamer renamer;
49 private final Map<TranslationDirection, Translator> translatorCache; 53 private final Map<TranslationDirection, Translator> translatorCache;
50 private Mappings mappings; 54 private Mappings mappings;
51 55
52 public Deobfuscator(JarFile jar) { 56 public Deobfuscator(JarFile jar) throws IOException {
53 this.jar = jar; 57 this.jar = jar;
58 this.parsedJar = new ParsedJar(jar);
54 59
55 // build the jar index 60 // build the jar index
56 this.jarIndex = new JarIndex(); 61 this.jarIndex = new JarIndex(entryPool);
57 this.jarIndex.indexJar(this.jar, true); 62 this.jarIndex.indexJar(this.parsedJar, true);
58 63
59 // config the decompiler 64 // config the decompiler
60 this.settings = DecompilerSettings.javaDefaults(); 65 this.settings = DecompilerSettings.javaDefaults();
61 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 66 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
62 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); 67 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
63 this.settings.setForceExplicitTypeArguments( 68 this.settings.setForceExplicitTypeArguments(
64 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); 69 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
65 // DEBUG 70 // DEBUG
66 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 71 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
67 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 72 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
68 73
69 // init defaults 74 // init defaults
70 this.translatorCache = Maps.newTreeMap(); 75 this.translatorCache = Maps.newTreeMap();
71 this.renamer = new MappingsRenamer(this.jarIndex, null); 76 this.renamer = new MappingsRenamer(this.jarIndex, null, this.entryPool);
72 // init mappings 77 // init mappings
73 setMappings(new Mappings()); 78 setMappings(new Mappings());
74 } 79 }
75 80
76 public JarFile getJar() { 81 public ParsedJar getJar() {
77 return this.jar; 82 return this.parsedJar;
78 } 83 }
79 84
80 public String getJarName() { 85 public String getJarName() {
@@ -102,16 +107,16 @@ public class Deobfuscator {
102 MappingsChecker checker = new MappingsChecker(this.jarIndex); 107 MappingsChecker checker = new MappingsChecker(this.jarIndex);
103 checker.dropBrokenMappings(val); 108 checker.dropBrokenMappings(val);
104 if (warnAboutDrops) { 109 if (warnAboutDrops) {
105 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 110 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
106 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 111 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
107 } 112 }
108 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { 113 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
109 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 114 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
110 } 115 }
111 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { 116 for (Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
112 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 117 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
113 } 118 }
114 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { 119 for (Map.Entry<MethodEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
115 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); 120 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
116 } 121 }
117 } 122 }
@@ -123,7 +128,7 @@ public class Deobfuscator {
123 128
124 public Translator getTranslator(TranslationDirection direction) { 129 public Translator getTranslator(TranslationDirection direction) {
125 return this.translatorCache.computeIfAbsent(direction, 130 return this.translatorCache.computeIfAbsent(direction,
126 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 131 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex()));
127 } 132 }
128 133
129 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 134 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
@@ -150,10 +155,11 @@ public class Deobfuscator {
150 155
151 public TranslatingTypeLoader createTypeLoader() { 156 public TranslatingTypeLoader createTypeLoader() {
152 return new TranslatingTypeLoader( 157 return new TranslatingTypeLoader(
153 this.jar, 158 this.parsedJar,
154 this.jarIndex, 159 this.jarIndex,
155 getTranslator(TranslationDirection.Obfuscating), 160 this.entryPool,
156 getTranslator(TranslationDirection.Deobfuscating) 161 getTranslator(TranslationDirection.OBFUSCATING),
162 getTranslator(TranslationDirection.DEOBFUSCATING)
157 ); 163 );
158 } 164 }
159 165
@@ -172,15 +178,15 @@ public class Deobfuscator {
172 deobfClassName = classMapping.getDeobfName(); 178 deobfClassName = classMapping.getDeobfName();
173 } 179 }
174 180
175 // set the type loader 181 // set the desc loader
176 TranslatingTypeLoader loader = createTypeLoader(); 182 TranslatingTypeLoader loader = createTypeLoader();
177 this.settings.setTypeLoader(loader); 183 this.settings.setTypeLoader(loader);
178 184
179 // see if procyon can find the type 185 // see if procyon can find the desc
180 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 186 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
181 if (type == null) { 187 if (type == null) {
182 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", 188 throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s",
183 className, deobfClassName, loader.getClassNamesToTry(deobfClassName) 189 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
184 )); 190 ));
185 } 191 }
186 TypeDefinition resolvedType = type.resolve(); 192 TypeDefinition resolvedType = type.resolve();
@@ -208,7 +214,7 @@ public class Deobfuscator {
208 } else { 214 } else {
209 index = new SourceIndex(source); 215 index = new SourceIndex(source);
210 } 216 }
211 sourceTree.acceptVisitor(new SourceIndexVisitor(), index); 217 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index);
212 218
213 // DEBUG 219 // DEBUG
214 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); 220 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
@@ -221,10 +227,10 @@ public class Deobfuscator {
221 Entry obfEntry = obfuscateEntry(deobfReference.entry); 227 Entry obfEntry = obfuscateEntry(deobfReference.entry);
222 228
223 // try to resolve the class 229 // try to resolve the class
224 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); 230 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryOwner(obfEntry);
225 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 231 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getOwnerClassEntry())) {
226 // change the class of the entry 232 // change the class of the entry
227 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 233 obfEntry = obfEntry.updateOwnership(resolvedObfClassEntry);
228 234
229 // save the new deobfuscated reference 235 // save the new deobfuscated reference
230 deobfReference.entry = deobfuscateEntry(obfEntry); 236 deobfReference.entry = deobfuscateEntry(obfEntry);
@@ -305,18 +311,14 @@ public class Deobfuscator {
305 } 311 }
306 } 312 }
307 313
308 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { 314 private boolean isMethodProvider(ClassEntry classObfEntry, MethodEntry methodEntry) {
309 if (behaviorEntry instanceof MethodEntry) { 315 Set<ClassEntry> classEntries = new HashSet<>();
310 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 316 addAllPotentialAncestors(classEntries, classObfEntry);
311 317
312 Set<ClassEntry> classEntries = new HashSet<>(); 318 for (ClassEntry parentEntry : classEntries) {
313 addAllPotentialAncestors(classEntries, classObfEntry); 319 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
314 320 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
315 for (ClassEntry parentEntry : classEntries) { 321 return false;
316 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature());
317 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) {
318 return false;
319 }
320 } 322 }
321 } 323 }
322 324
@@ -332,7 +334,7 @@ public class Deobfuscator {
332 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { 334 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) {
333 progress.onProgress(i++, classMapping.getDeobfName()); 335 progress.onProgress(i++, classMapping.getDeobfName());
334 rebuildMethodNames(classMapping, renameClassMap); 336 rebuildMethodNames(classMapping, renameClassMap);
335 for(ClassMapping innerClass : classMapping.innerClasses()){ 337 for (ClassMapping innerClass : classMapping.innerClasses()) {
336 rebuildMethodNames(innerClass, renameClassMap); 338 rebuildMethodNames(innerClass, renameClassMap);
337 } 339 }
338 } 340 }
@@ -356,29 +358,29 @@ public class Deobfuscator {
356 358
357 try { 359 try {
358 rename(obfEntry, name); 360 rename(obfEntry, name);
359 } catch (IllegalNameException exception) 361 } catch (IllegalNameException exception) {
360 {
361 System.out.println("WARNING: " + exception.getMessage()); 362 System.out.println("WARNING: " + exception.getMessage());
362 } 363 }
363 } 364 }
364 } 365 }
365 } 366 }
366 367
367 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap){ 368 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) {
368 Map<Entry, String> renameEntries = new HashMap<>(); 369 Map<Entry, String> renameEntries = new HashMap<>();
369 370
370 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 371 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
371 ClassEntry classObfEntry = classMapping.getObfEntry(); 372 ClassEntry classObfEntry = classMapping.getObfEntry();
372 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); 373 MethodEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
373 374
374 if (isBehaviorProvider(classObfEntry, obfEntry)) { 375 if (isMethodProvider(classObfEntry, obfEntry)) {
375 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) 376 if (hasDeobfuscatedName(obfEntry)
376 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { 377 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
377 renameEntries.put(obfEntry, methodMapping.getDeobfName()); 378 renameEntries.put(obfEntry, methodMapping.getDeobfName());
378 } 379 }
379 380
380 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { 381 ArrayList<LocalVariableMapping> arguments = Lists.newArrayList(methodMapping.arguments());
381 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); 382 for (LocalVariableMapping localVariableMapping : arguments) {
383 Entry argObfEntry = localVariableMapping.getObfEntry(obfEntry);
382 if (hasDeobfuscatedName(argObfEntry)) { 384 if (hasDeobfuscatedName(argObfEntry)) {
383 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); 385 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
384 } 386 }
@@ -390,45 +392,44 @@ public class Deobfuscator {
390 } 392 }
391 393
392 394
393
394 public void writeJar(File out, ProgressListener progress) { 395 public void writeJar(File out, ProgressListener progress) {
395 transformJar(out, progress, createTypeLoader()::transformClass); 396 transformJar(out, progress, createTypeLoader()::createTransformer);
396 } 397 }
397 398
398 public void protectifyJar(File out, ProgressListener progress) { 399 public void protectifyJar(File out, ProgressListener progress) {
399 transformJar(out, progress, ClassProtectifier::protectify); 400 transformJar(out, progress, (node, writer) -> node.accept(new ClassProtectifier(Opcodes.ASM5, writer)));
400 } 401 }
401 402
402 public void publifyJar(File out, ProgressListener progress) { 403 public void publifyJar(File out, ProgressListener progress) {
403 transformJar(out, progress, ClassPublifier::publify); 404 transformJar(out, progress, (node, writer) -> node.accept(new ClassPublifier(Opcodes.ASM5, writer)));
404 } 405 }
405 406
406 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 407 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
407 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 408 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
408 if (progress != null) { 409 if (progress != null) {
409 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); 410 progress.init(parsedJar.getClassCount(), "Transforming classes...");
410 } 411 }
411 412
412 int i = 0; 413 AtomicInteger i = new AtomicInteger();
413 for (CtClass c : JarClassIterator.classes(this.jar)) { 414 parsedJar.visit(node -> {
414 if (progress != null) { 415 if (progress != null) {
415 progress.onProgress(i++, c.getName()); 416 progress.onProgress(i.getAndIncrement(), node.name);
416 } 417 }
417 418
418 try { 419 try {
419 c = transformer.transform(c); 420 ClassWriter writer = new ClassWriter(0);
420 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); 421 transformer.write(node, writer);
421 outJar.write(c.toBytecode()); 422 outJar.putNextEntry(new JarEntry(node.name.replace('.', '/') + ".class"));
423 outJar.write(writer.toByteArray());
422 outJar.closeEntry(); 424 outJar.closeEntry();
423 } catch (Throwable t) { 425 } catch (Throwable t) {
424 throw new Error("Unable to transform class " + c.getName(), t); 426 throw new Error("Unable to transform class " + node.name, t);
425 } 427 }
426 } 428 });
429
427 if (progress != null) { 430 if (progress != null) {
428 progress.onProgress(i, "Done!"); 431 progress.onProgress(i.get(), "Done!");
429 } 432 }
430
431 outJar.close();
432 } catch (IOException ex) { 433 } catch (IOException ex) {
433 throw new Error("Unable to write to Jar file!"); 434 throw new Error("Unable to write to Jar file!");
434 } 435 }
@@ -438,14 +439,22 @@ public class Deobfuscator {
438 if (deobfEntry == null) { 439 if (deobfEntry == null) {
439 return null; 440 return null;
440 } 441 }
441 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); 442 T translatedEntry = getTranslator(TranslationDirection.OBFUSCATING).getTranslatedEntry(deobfEntry);
443 if (translatedEntry == null) {
444 return deobfEntry;
445 }
446 return translatedEntry;
442 } 447 }
443 448
444 public <T extends Entry> T deobfuscateEntry(T obfEntry) { 449 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
445 if (obfEntry == null) { 450 if (obfEntry == null) {
446 return null; 451 return null;
447 } 452 }
448 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); 453 T translatedEntry = getTranslator(TranslationDirection.DEOBFUSCATING).getTranslatedEntry(obfEntry);
454 if (translatedEntry == null) {
455 return obfEntry;
456 }
457 return translatedEntry;
449 } 458 }
450 459
451 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { 460 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
@@ -473,7 +482,7 @@ public class Deobfuscator {
473 // HACKHACK: Object methods are not obfuscated identifiers 482 // HACKHACK: Object methods are not obfuscated identifiers
474 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 483 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
475 String name = obfMethodEntry.getName(); 484 String name = obfMethodEntry.getName();
476 String sig = obfMethodEntry.getSignature().toString(); 485 String sig = obfMethodEntry.getDesc().toString();
477 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { 486 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
478 return false; 487 return false;
479 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { 488 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
@@ -499,7 +508,7 @@ public class Deobfuscator {
499 } 508 }
500 509
501 // FIXME: HACK EVEN MORE HACK! 510 // FIXME: HACK EVEN MORE HACK!
502 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) 511 if (hack && this.jarIndex.containsObfEntry(obfEntry.getOwnerClassEntry()))
503 return true; 512 return true;
504 } 513 }
505 514
@@ -515,27 +524,24 @@ public class Deobfuscator {
515 } 524 }
516 525
517 public boolean hasDeobfuscatedName(Entry obfEntry) { 526 public boolean hasDeobfuscatedName(Entry obfEntry) {
518 Translator translator = getTranslator(TranslationDirection.Deobfuscating); 527 Translator translator = getTranslator(TranslationDirection.DEOBFUSCATING);
519 if (obfEntry instanceof ClassEntry) { 528 if (obfEntry instanceof ClassEntry) {
520 ClassEntry obfClass = (ClassEntry) obfEntry; 529 ClassEntry obfClass = (ClassEntry) obfEntry;
521 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); 530 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
522 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); 531 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
523 return classMapping != null && classMapping.getDeobfName() != null; 532 return classMapping != null && classMapping.getDeobfName() != null;
524 } else if (obfEntry instanceof FieldEntry) { 533 } else if (obfEntry instanceof FieldEntry) {
525 return translator.translate((FieldEntry) obfEntry) != null; 534 return translator.getTranslatedField((FieldEntry) obfEntry) != null;
526 } else if (obfEntry instanceof MethodEntry) { 535 } else if (obfEntry instanceof MethodEntry) {
527 return translator.translate((MethodEntry) obfEntry) != null; 536 MethodEntry methodEntry = (MethodEntry) obfEntry;
528 } else if (obfEntry instanceof ConstructorEntry) { 537 if (methodEntry.isConstructor()) {
529 // constructors have no names 538 return false;
530 return false; 539 }
531 } else if (obfEntry instanceof ArgumentEntry) { 540 return translator.getTranslatedMethod(methodEntry) != null;
532 return translator.translate((ArgumentEntry) obfEntry) != null;
533 } else if (obfEntry instanceof LocalVariableEntry) { 541 } else if (obfEntry instanceof LocalVariableEntry) {
534 // TODO: Implement it 542 return translator.getTranslatedVariable((LocalVariableEntry) obfEntry) != null;
535 //return translator.translate((LocalVariableEntry)obfEntry) != null;
536 return false;
537 } else { 543 } else {
538 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 544 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
539 } 545 }
540 } 546 }
541 547
@@ -547,19 +553,18 @@ public class Deobfuscator {
547 553
548 public void rename(Entry obfEntry, String newName, boolean clearCache) { 554 public void rename(Entry obfEntry, String newName, boolean clearCache) {
549 if (obfEntry instanceof ClassEntry) { 555 if (obfEntry instanceof ClassEntry) {
550 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 556 this.renamer.setClassName((ClassEntry) obfEntry, newName);
551 } else if (obfEntry instanceof FieldEntry) { 557 } else if (obfEntry instanceof FieldEntry) {
552 this.renamer.setFieldName((FieldEntry) obfEntry, newName); 558 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
553 } else if (obfEntry instanceof MethodEntry) { 559 } else if (obfEntry instanceof MethodEntry) {
560 if (((MethodEntry) obfEntry).isConstructor()) {
561 throw new IllegalArgumentException("Cannot rename constructors");
562 }
554 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); 563 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
555 } else if (obfEntry instanceof ConstructorEntry) {
556 throw new IllegalArgumentException("Cannot rename constructors");
557 } else if (obfEntry instanceof ArgumentEntry) {
558 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName);
559 } else if (obfEntry instanceof LocalVariableEntry) { 564 } else if (obfEntry instanceof LocalVariableEntry) {
560 // TODO: Implement it 565 this.renamer.setLocalVariableTreeName((LocalVariableEntry) obfEntry, newName);
561 } else { 566 } else {
562 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 567 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
563 } 568 }
564 569
565 // clear caches 570 // clear caches
@@ -573,13 +578,14 @@ public class Deobfuscator {
573 } else if (obfEntry instanceof FieldEntry) { 578 } else if (obfEntry instanceof FieldEntry) {
574 this.renamer.removeFieldMapping((FieldEntry) obfEntry); 579 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
575 } else if (obfEntry instanceof MethodEntry) { 580 } else if (obfEntry instanceof MethodEntry) {
581 if (((MethodEntry) obfEntry).isConstructor()) {
582 throw new IllegalArgumentException("Cannot rename constructors");
583 }
576 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); 584 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
577 } else if (obfEntry instanceof ConstructorEntry) { 585 } else if (obfEntry instanceof LocalVariableEntry) {
578 throw new IllegalArgumentException("Cannot rename constructors"); 586 this.renamer.removeLocalVariableMapping((LocalVariableEntry) obfEntry);
579 } else if (obfEntry instanceof ArgumentEntry) {
580 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry);
581 } else { 587 } else {
582 throw new Error("Unknown entry type: " + obfEntry); 588 throw new Error("Unknown entry desc: " + obfEntry);
583 } 589 }
584 590
585 // clear caches 591 // clear caches
@@ -592,15 +598,15 @@ public class Deobfuscator {
592 } else if (obfEntry instanceof FieldEntry) { 598 } else if (obfEntry instanceof FieldEntry) {
593 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); 599 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
594 } else if (obfEntry instanceof MethodEntry) { 600 } else if (obfEntry instanceof MethodEntry) {
595 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); 601 MethodEntry methodEntry = (MethodEntry) obfEntry;
596 } else if (obfEntry instanceof ConstructorEntry) { 602 if (methodEntry.isConstructor()) {
597 throw new IllegalArgumentException("Cannot rename constructors"); 603 throw new IllegalArgumentException("Cannot rename constructors");
598 } else if (obfEntry instanceof ArgumentEntry) { 604 }
599 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 605 this.renamer.markMethodTreeAsDeobfuscated(methodEntry);
600 } else if (obfEntry instanceof LocalVariableEntry) { 606 } else if (obfEntry instanceof LocalVariableEntry) {
601 // TODO: Implement it 607 this.renamer.markArgumentAsDeobfuscated((LocalVariableEntry) obfEntry);
602 } else { 608 } else {
603 throw new Error("Unknown entry type: " + obfEntry); 609 throw new Error("Unknown entry desc: " + obfEntry);
604 } 610 }
605 611
606 // clear caches 612 // clear caches
@@ -613,17 +619,24 @@ public class Deobfuscator {
613 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); 619 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
614 else if (obfEntry instanceof FieldEntry) 620 else if (obfEntry instanceof FieldEntry)
615 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); 621 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
616 else if (obfEntry instanceof BehaviorEntry) 622 else if (obfEntry instanceof MethodEntry)
617 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); 623 this.renamer.setMethodModifier((MethodEntry) obfEntry, modifierEntry);
618 else 624 else
619 throw new Error("Unknown entry type: " + obfEntry); 625 throw new Error("Unknown entry desc: " + obfEntry);
620 } 626 }
621 627
622 public Mappings.EntryModifier getModifier(Entry obEntry) { 628 public Mappings.EntryModifier getModifier(Entry obfEntry) {
623 Entry entry = obfuscateEntry(obEntry); 629 Entry entry = obfuscateEntry(obfEntry);
624 if (entry != null) 630 if (entry != null)
625 obEntry = entry; 631 obfEntry = entry;
626 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); 632 if (obfEntry instanceof ClassEntry)
633 return this.renamer.getClassModifier((ClassEntry) obfEntry);
634 else if (obfEntry instanceof FieldEntry)
635 return this.renamer.getFieldModifier((FieldEntry) obfEntry);
636 else if (obfEntry instanceof MethodEntry)
637 return this.renamer.getMethodModfifier((MethodEntry) obfEntry);
638 else
639 throw new Error("Unknown entry desc: " + obfEntry);
627 } 640 }
628 641
629 public interface ProgressListener { 642 public interface ProgressListener {
@@ -633,6 +646,6 @@ public class Deobfuscator {
633 } 646 }
634 647
635 public interface ClassTransformer { 648 public interface ClassTransformer {
636 CtClass transform(CtClass c) throws Exception; 649 void write(ClassNode node, ClassWriter writer);
637 } 650 }
638} 651}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 2a2041a..c91070f 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -16,37 +16,33 @@ import com.google.common.collect.Maps;
16import com.strobel.assembler.metadata.Buffer; 16import com.strobel.assembler.metadata.Buffer;
17import com.strobel.assembler.metadata.ClasspathTypeLoader; 17import com.strobel.assembler.metadata.ClasspathTypeLoader;
18import com.strobel.assembler.metadata.ITypeLoader; 18import com.strobel.assembler.metadata.ITypeLoader;
19import cuchaz.enigma.analysis.BridgeMarker;
20import cuchaz.enigma.analysis.JarIndex; 19import cuchaz.enigma.analysis.JarIndex;
21import cuchaz.enigma.bytecode.translators.ClassTranslator; 20import cuchaz.enigma.analysis.ParsedJar;
22import cuchaz.enigma.bytecode.translators.InnerClassWriter; 21import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
23import cuchaz.enigma.bytecode.translators.LocalVariableTranslator;
24import cuchaz.enigma.bytecode.translators.MethodParameterTranslator;
25import cuchaz.enigma.mapping.ClassEntry; 22import cuchaz.enigma.mapping.ClassEntry;
23import cuchaz.enigma.mapping.ReferencedEntryPool;
26import cuchaz.enigma.mapping.Translator; 24import cuchaz.enigma.mapping.Translator;
27import javassist.*; 25import org.objectweb.asm.ClassWriter;
28import javassist.bytecode.Descriptor; 26import org.objectweb.asm.Opcodes;
27import org.objectweb.asm.tree.ClassNode;
29 28
30import java.io.ByteArrayOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.util.List; 29import java.util.List;
34import java.util.Map; 30import java.util.Map;
35import java.util.jar.JarEntry;
36import java.util.jar.JarFile;
37 31
38public class TranslatingTypeLoader implements ITypeLoader { 32public class TranslatingTypeLoader implements ITypeLoader {
39 33
40 private JarFile jar; 34 private final ParsedJar jar;
41 private JarIndex jarIndex; 35 private final JarIndex jarIndex;
42 private Translator obfuscatingTranslator; 36 private final ReferencedEntryPool entryPool;
43 private Translator deobfuscatingTranslator; 37 private final Translator obfuscatingTranslator;
44 private Map<String, byte[]> cache; 38 private final Translator deobfuscatingTranslator;
45 private ClasspathTypeLoader defaultTypeLoader; 39 private final Map<String, byte[]> cache;
40 private final ClasspathTypeLoader defaultTypeLoader;
46 41
47 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { 42 public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) {
48 this.jar = jar; 43 this.jar = jar;
49 this.jarIndex = jarIndex; 44 this.jarIndex = jarIndex;
45 this.entryPool = entryPool;
50 this.obfuscatingTranslator = obfuscatingTranslator; 46 this.obfuscatingTranslator = obfuscatingTranslator;
51 this.deobfuscatingTranslator = deobfuscatingTranslator; 47 this.deobfuscatingTranslator = deobfuscatingTranslator;
52 this.cache = Maps.newHashMap(); 48 this.cache = Maps.newHashMap();
@@ -71,7 +67,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
71 } 67 }
72 68
73 if (data == null) { 69 if (data == null) {
74 // chain to default type loader 70 // chain to default desc loader
75 return this.defaultTypeLoader.tryLoadType(className, out); 71 return this.defaultTypeLoader.tryLoadType(className, out);
76 } 72 }
77 73
@@ -82,36 +78,18 @@ public class TranslatingTypeLoader implements ITypeLoader {
82 return true; 78 return true;
83 } 79 }
84 80
85 public CtClass loadClass(String deobfClassName) {
86
87 byte[] data = loadType(deobfClassName);
88 if (data == null) {
89 return null;
90 }
91
92 // return a javassist handle for the class
93 String javaClassFileName = Descriptor.toJavaName(deobfClassName);
94 ClassPool classPool = new ClassPool();
95 classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data));
96 try {
97 return classPool.get(javaClassFileName);
98 } catch (NotFoundException ex) {
99 throw new Error(ex);
100 }
101 }
102
103 private byte[] loadType(String className) { 81 private byte[] loadType(String className) {
104 82
105 // NOTE: don't know if class name is obf or deobf 83 // NOTE: don't know if class name is obf or deobf
106 ClassEntry classEntry = new ClassEntry(className); 84 ClassEntry classEntry = new ClassEntry(className);
107 ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry); 85 ClassEntry obfClassEntry = this.obfuscatingTranslator.getTranslatedClass(classEntry);
108 86
109 // is this an inner class referenced directly? (ie trying to load b instead of a$b) 87 // is this an inner class referenced directly? (ie trying to load b instead of a$b)
110 if (!obfClassEntry.isInnerClass()) { 88 if (!obfClassEntry.isInnerClass()) {
111 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry); 89 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry);
112 if (classChain.size() > 1) { 90 if (classChain.size() > 1) {
113 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", 91 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s",
114 className, obfClassEntry.buildClassEntry(classChain) 92 className, obfClassEntry.buildClassEntry(classChain)
115 )); 93 ));
116 return null; 94 return null;
117 } 95 }
@@ -126,56 +104,26 @@ public class TranslatingTypeLoader implements ITypeLoader {
126 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); 104 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName()));
127 105
128 // find the class in the jar 106 // find the class in the jar
129 String classInJarName = findClassInJar(obfClassEntry); 107 ClassNode node = findClassInJar(obfClassEntry);
130 if (classInJarName == null) { 108 if (node == null) {
131 // couldn't find it 109 // couldn't find it
132 return null; 110 return null;
133 } 111 }
134 112
135 try { 113 ClassWriter writer = new ClassWriter(0);
136 // read the class file into a buffer 114 createTransformer(node, writer);
137 ByteArrayOutputStream data = new ByteArrayOutputStream();
138 byte[] buf = new byte[1024 * 1024]; // 1 KiB
139 InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class"));
140 while (true) {
141 int bytesRead = in.read(buf);
142 if (bytesRead <= 0) {
143 break;
144 }
145 data.write(buf, 0, bytesRead);
146 }
147 data.close();
148 in.close();
149 buf = data.toByteArray();
150
151 // load the javassist handle to the raw class
152 ClassPool classPool = new ClassPool();
153 String classInJarJavaName = Descriptor.toJavaName(classInJarName);
154 classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf));
155 CtClass c = classPool.get(classInJarJavaName);
156
157 c = transformClass(c);
158 115
159 // sanity checking 116 // we have a transformed class!
160 assertClassName(c, classEntry); 117 return writer.toByteArray();
161
162 // DEBUG
163 //Util.writeClass( c );
164
165 // we have a transformed class!
166 return c.toBytecode();
167 } catch (IOException | NotFoundException | CannotCompileException ex) {
168 throw new Error(ex);
169 }
170 } 118 }
171 119
172 private String findClassInJar(ClassEntry obfClassEntry) { 120 private ClassNode findClassInJar(ClassEntry obfClassEntry) {
173 121
174 // try to find the class in the jar 122 // try to find the class in the jar
175 for (String className : getClassNamesToTry(obfClassEntry)) { 123 for (String className : getClassNamesToTry(obfClassEntry)) {
176 JarEntry jarEntry = this.jar.getJarEntry(className + ".class"); 124 ClassNode node = this.jar.getClassNode(className);
177 if (jarEntry != null) { 125 if (node != null) {
178 return className; 126 return node;
179 } 127 }
180 } 128 }
181 129
@@ -184,7 +132,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
184 } 132 }
185 133
186 public List<String> getClassNamesToTry(String className) { 134 public List<String> getClassNamesToTry(String className) {
187 return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className))); 135 return getClassNamesToTry(this.obfuscatingTranslator.getTranslatedClass(new ClassEntry(className)));
188 } 136 }
189 137
190 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 138 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
@@ -197,36 +145,7 @@ public class TranslatingTypeLoader implements ITypeLoader {
197 return classNamesToTry; 145 return classNamesToTry;
198 } 146 }
199 147
200 public CtClass transformClass(CtClass c) 148 public void createTransformer(ClassNode node, ClassWriter writer) {
201 throws IOException, NotFoundException, CannotCompileException { 149 node.accept(new TranslationClassVisitor(deobfuscatingTranslator, jarIndex, entryPool, Opcodes.ASM5, writer));
202
203 // reconstruct inner classes
204 InnerClassWriter.write(jarIndex, c);
205
206 // re-get the javassist handle since we changed class names
207 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
208 String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName());
209 ClassPool classPool = new ClassPool();
210 classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode()));
211 c = classPool.get(javaClassReconstructedName);
212
213 // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong)
214 assertClassName(c, obfClassEntry);
215
216 // do all kinds of deobfuscating transformations on the class
217 BridgeMarker.markBridges(this.jarIndex, c);
218 MethodParameterTranslator.translate(this.deobfuscatingTranslator, c);
219 LocalVariableTranslator.translate(this.deobfuscatingTranslator, c);
220 ClassTranslator.translate(this.deobfuscatingTranslator, c);
221
222 return c;
223 }
224
225 private void assertClassName(CtClass c, ClassEntry obfClassEntry) {
226 String name1 = Descriptor.toJvmName(c.getName());
227 assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1);
228
229 String name2 = Descriptor.toJvmName(c.getClassFile().getName());
230 assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2);
231 } 150 }
232} 151}
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index 547d85e..8181418 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -11,8 +11,7 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import javassist.CtBehavior; 14import cuchaz.enigma.bytecode.AccessFlags;
15import javassist.CtField;
16 15
17import java.lang.reflect.Modifier; 16import java.lang.reflect.Modifier;
18 17
@@ -20,12 +19,8 @@ public enum Access {
20 19
21 PUBLIC, PROTECTED, PACKAGE, PRIVATE; 20 PUBLIC, PROTECTED, PACKAGE, PRIVATE;
22 21
23 public static Access get(CtBehavior behavior) { 22 public static Access get(AccessFlags flags) {
24 return get(behavior.getModifiers()); 23 return get(flags.getFlags());
25 }
26
27 public static Access get(CtField field) {
28 return get(field.getModifiers());
29 } 24 }
30 25
31 public static Access get(int modifiers) { 26 public static Access get(int modifiers) {
diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
deleted file mode 100644
index a2f1f90..0000000
--- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java
+++ /dev/null
@@ -1,38 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.mapping.EntryFactory;
15import cuchaz.enigma.mapping.MethodEntry;
16import javassist.CtClass;
17import javassist.CtMethod;
18import javassist.bytecode.AccessFlag;
19
20public class BridgeMarker {
21
22 public static void markBridges(JarIndex jarIndex, CtClass c) {
23
24 for (CtMethod method : c.getDeclaredMethods()) {
25 MethodEntry methodEntry = EntryFactory.getMethodEntry(method);
26
27 // is this a bridge method?
28 MethodEntry bridgedMethodEntry = jarIndex.getBridgedMethod(methodEntry);
29 if (bridgedMethodEntry != null) {
30
31 // it's a bridge method! add the bridge flag
32 int flags = method.getMethodInfo().getAccessFlags();
33 flags |= AccessFlag.BRIDGE;
34 method.getMethodInfo().setAccessFlags(flags);
35 }
36 }
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index f2fb2f8..d0e1ddb 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -21,8 +21,8 @@ import java.util.List;
21 21
22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
23 23
24 private Translator deobfuscatingTranslator; 24 private final Translator deobfuscatingTranslator;
25 private ClassEntry entry; 25 private final ClassEntry entry;
26 26
27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 28 this.deobfuscatingTranslator = deobfuscatingTranslator;
@@ -31,7 +31,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
31 31
32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
33 // is this the node? 33 // is this the node?
34 if (node.entry.equals(entry.getClassEntry())) { 34 if (node.entry.equals(entry.getOwnerClassEntry())) {
35 return node; 35 return node;
36 } 36 }
37 37
@@ -50,7 +50,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
50 } 50 }
51 51
52 public String getDeobfClassName() { 52 public String getDeobfClassName() {
53 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 53 return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName();
54 } 54 }
55 55
56 @Override 56 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index 24e7cb0..8fd71b7 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -20,12 +20,12 @@ import java.util.List;
20 20
21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
22 22
23 private Translator deobfuscatingTranslator; 23 private final Translator deobfuscatingTranslator;
24 private String obfClassName; 24 private final ClassEntry obfClassEntry;
25 25
26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.deobfuscatingTranslator = deobfuscatingTranslator;
28 this.obfClassName = obfClassName; 28 this.obfClassEntry = new ClassEntry(obfClassName);
29 } 29 }
30 30
31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
@@ -45,11 +45,11 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
45 } 45 }
46 46
47 public String getObfClassName() { 47 public String getObfClassName() {
48 return this.obfClassName; 48 return this.obfClassEntry.getClassName();
49 } 49 }
50 50
51 public String getDeobfClassName() { 51 public String getDeobfClassName() {
52 return this.deobfuscatingTranslator.translateClass(this.obfClassName); 52 return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName();
53 } 53 }
54 54
55 @Override 55 @Override
@@ -58,13 +58,13 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
58 if (deobfClassName != null) { 58 if (deobfClassName != null) {
59 return deobfClassName; 59 return deobfClassName;
60 } 60 }
61 return this.obfClassName; 61 return this.obfClassEntry.getName();
62 } 62 }
63 63
64 public void load(TranslationIndex ancestries, boolean recurse) { 64 public void load(TranslationIndex ancestries, boolean recurse) {
65 // get all the child nodes 65 // get all the child nodes
66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
67 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { 67 for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) {
68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); 68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName()));
69 } 69 }
70 70
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index 3761fca..b972585 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -12,8 +12,8 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.ClassEntry;
15import cuchaz.enigma.mapping.ConstructorEntry;
16import cuchaz.enigma.mapping.Entry; 15import cuchaz.enigma.mapping.Entry;
16import cuchaz.enigma.mapping.MethodEntry;
17import cuchaz.enigma.utils.Utils; 17import cuchaz.enigma.utils.Utils;
18 18
19import java.util.Arrays; 19import java.util.Arrays;
@@ -21,7 +21,7 @@ import java.util.List;
21 21
22public class EntryReference<E extends Entry, C extends Entry> { 22public class EntryReference<E extends Entry, C extends Entry> {
23 23
24 private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); 24 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static");
25 public E entry; 25 public E entry;
26 public C context; 26 public C context;
27 27
@@ -40,7 +40,7 @@ public class EntryReference<E extends Entry, C extends Entry> {
40 this.context = context; 40 this.context = context;
41 41
42 this.sourceName = sourceName != null && !sourceName.isEmpty(); 42 this.sourceName = sourceName != null && !sourceName.isEmpty();
43 if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { 43 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)) {
44 this.sourceName = false; 44 this.sourceName = false;
45 } 45 }
46 } 46 }
@@ -53,9 +53,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
53 53
54 public ClassEntry getLocationClassEntry() { 54 public ClassEntry getLocationClassEntry() {
55 if (context != null) { 55 if (context != null) {
56 return context.getClassEntry(); 56 return context.getOwnerClassEntry();
57 } 57 }
58 return entry.getClassEntry(); 58 return entry.getOwnerClassEntry();
59 } 59 }
60 60
61 public boolean isNamed() { 61 public boolean isNamed() {
@@ -63,9 +63,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
63 } 63 }
64 64
65 public Entry getNameableEntry() { 65 public Entry getNameableEntry() {
66 if (entry instanceof ConstructorEntry) { 66 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) {
67 // renaming a constructor really means renaming the class 67 // renaming a constructor really means renaming the class
68 return entry.getClassEntry(); 68 return entry.getOwnerClassEntry();
69 } 69 }
70 return entry; 70 return entry;
71 } 71 }
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
index 75806c3..b0bcc91 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -87,18 +87,18 @@ public class EntryRenamer {
87 MethodEntry newMethodEntry = renames.get(methodEntry); 87 MethodEntry newMethodEntry = renames.get(methodEntry);
88 if (newMethodEntry != null) { 88 if (newMethodEntry != null) {
89 return (T) new MethodEntry( 89 return (T) new MethodEntry(
90 methodEntry.getClassEntry(), 90 methodEntry.getOwnerClassEntry(),
91 newMethodEntry.getName(), 91 newMethodEntry.getName(),
92 methodEntry.getSignature() 92 methodEntry.getDesc()
93 ); 93 );
94 } 94 }
95 return thing; 95 return thing;
96 } else if (thing instanceof ArgumentEntry) { 96 } else if (thing instanceof LocalVariableEntry) {
97 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 97 LocalVariableEntry variableEntry = (LocalVariableEntry) thing;
98 return (T) new ArgumentEntry( 98 return (T) new LocalVariableEntry(
99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), 99 renameMethodsInThing(renames, variableEntry.getOwnerEntry()),
100 argumentEntry.getIndex(), 100 variableEntry.getIndex(),
101 argumentEntry.getName() 101 variableEntry.getName()
102 ); 102 );
103 } else if (thing instanceof EntryReference) { 103 } else if (thing instanceof EntryReference) {
104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
@@ -119,27 +119,24 @@ public class EntryRenamer {
119 } else if (thing instanceof ClassEntry) { 119 } else if (thing instanceof ClassEntry) {
120 ClassEntry classEntry = (ClassEntry) thing; 120 ClassEntry classEntry = (ClassEntry) thing;
121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); 121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
122 } else if (thing instanceof FieldEntry) { 122 } else if (thing instanceof FieldDefEntry) {
123 FieldEntry fieldEntry = (FieldEntry) thing; 123 FieldDefEntry fieldEntry = (FieldDefEntry) thing;
124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); 124 return (T) new FieldDefEntry(renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getDesc()), fieldEntry.getAccess());
125 } else if (thing instanceof ConstructorEntry) { 125 } else if (thing instanceof MethodDefEntry) {
126 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 126 MethodDefEntry methodEntry = (MethodDefEntry) thing;
127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); 127 return (T) new MethodDefEntry(renameClassesInThing(renames, methodEntry.getOwnerClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getDesc()), methodEntry.getAccess());
128 } else if (thing instanceof MethodEntry) { 128 } else if (thing instanceof LocalVariableEntry) {
129 MethodEntry methodEntry = (MethodEntry) thing; 129 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); 130 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName());
131 } else if (thing instanceof ArgumentEntry) {
132 ArgumentEntry argumentEntry = (ArgumentEntry) thing;
133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName());
134 } else if (thing instanceof EntryReference) { 131 } else if (thing instanceof EntryReference) {
135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 132 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
136 reference.entry = renameClassesInThing(renames, reference.entry); 133 reference.entry = renameClassesInThing(renames, reference.entry);
137 reference.context = renameClassesInThing(renames, reference.context); 134 reference.context = renameClassesInThing(renames, reference.context);
138 return thing; 135 return thing;
139 } else if (thing instanceof Signature) { 136 } else if (thing instanceof MethodDescriptor) {
140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); 137 return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
141 } else if (thing instanceof Type) { 138 } else if (thing instanceof TypeDescriptor) {
142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); 139 return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
143 } 140 }
144 141
145 return thing; 142 return thing;
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 34d2eff..3e467db 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -11,17 +11,15 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.*;
15import cuchaz.enigma.mapping.FieldEntry;
16import cuchaz.enigma.mapping.Translator;
17 15
18import javax.swing.tree.DefaultMutableTreeNode; 16import javax.swing.tree.DefaultMutableTreeNode;
19 17
20public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 18public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
21 19
22 private Translator deobfuscatingTranslator; 20 private Translator deobfuscatingTranslator;
23 private FieldEntry entry; 21 private FieldEntry entry;
24 private EntryReference<FieldEntry, BehaviorEntry> reference; 22 private EntryReference<FieldEntry, MethodDefEntry> reference;
25 private Access access; 23 private Access access;
26 24
27 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 25 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) {
@@ -30,7 +28,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
30 this.reference = null; 28 this.reference = null;
31 } 29 }
32 30
33 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { 31 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, Access access) {
34 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.deobfuscatingTranslator = deobfuscatingTranslator;
35 this.entry = reference.entry; 33 this.entry = reference.entry;
36 this.reference = reference; 34 this.reference = reference;
@@ -43,34 +41,34 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
43 } 41 }
44 42
45 @Override 43 @Override
46 public EntryReference<FieldEntry, BehaviorEntry> getReference() { 44 public EntryReference<FieldEntry, MethodDefEntry> getReference() {
47 return this.reference; 45 return this.reference;
48 } 46 }
49 47
50 @Override 48 @Override
51 public String toString() { 49 public String toString() {
52 if (this.reference != null) { 50 if (this.reference != null) {
53 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); 51 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access);
54 } 52 }
55 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 53 return deobfuscatingTranslator.getTranslatedField(entry).getName();
56 } 54 }
57 55
58 public void load(JarIndex index, boolean recurse) { 56 public void load(JarIndex index, boolean recurse) {
59 // get all the child nodes 57 // get all the child nodes
60 if (this.reference == null) { 58 if (this.reference == null) {
61 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { 59 for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) {
62 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 60 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
63 } 61 }
64 } else { 62 } else {
65 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { 63 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.reference.context)) {
66 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 64 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
67 } 65 }
68 } 66 }
69 67
70 if (recurse && children != null) { 68 if (recurse && children != null) {
71 for (Object node : children) { 69 for (Object node : children) {
72 if (node instanceof BehaviorReferenceTreeNode) { 70 if (node instanceof MethodReferenceTreeNode) {
73 ((BehaviorReferenceTreeNode) node).load(index, true); 71 ((MethodReferenceTreeNode) node).load(index, true);
74 } else if (node instanceof FieldReferenceTreeNode) { 72 } else if (node instanceof FieldReferenceTreeNode) {
75 ((FieldReferenceTreeNode) node).load(index, true); 73 ((FieldReferenceTreeNode) node).load(index, true);
76 } 74 }
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
new file mode 100644
index 0000000..97d6ffa
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
@@ -0,0 +1,38 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.ClassDefEntry;
4import org.objectweb.asm.ClassVisitor;
5import org.objectweb.asm.FieldVisitor;
6import org.objectweb.asm.MethodVisitor;
7
8public class IndexClassVisitor extends ClassVisitor {
9 private final JarIndex index;
10 private ClassDefEntry classEntry;
11
12 public IndexClassVisitor(JarIndex index, int api) {
13 super(api);
14 this.index = index;
15 }
16
17 @Override
18 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
19 this.classEntry = this.index.indexClass(access, name, superName, interfaces);
20 super.visit(version, access, name, signature, superName, interfaces);
21 }
22
23 @Override
24 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
25 if (this.classEntry != null) {
26 this.index.indexField(this.classEntry, access, name, desc);
27 }
28 return super.visitField(access, name, desc, signature, value);
29 }
30
31 @Override
32 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
33 if (this.classEntry != null) {
34 this.index.indexMethod(this.classEntry, access, name, desc);
35 }
36 return super.visitMethod(access, name, desc, signature, exceptions);
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
new file mode 100644
index 0000000..621bd33
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
@@ -0,0 +1,23 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.ClassEntry;
4import org.objectweb.asm.ClassVisitor;
5
6public class IndexInnerClassVisitor extends ClassVisitor {
7 private final JarIndex index;
8
9 public IndexInnerClassVisitor(JarIndex index, int api) {
10 super(api);
11 this.index = index;
12 }
13
14 @Override
15 public void visitInnerClass(String name, String outerName, String innerName, int access) {
16 ClassEntry entry = new ClassEntry(name);
17 // Ignore anonymous classes
18 if (innerName != null && outerName != null) {
19 ClassEntry outerEntry = new ClassEntry(outerName);
20 index.indexInnerClass(entry, outerEntry);
21 }
22 }
23}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
new file mode 100644
index 0000000..552601b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
@@ -0,0 +1,50 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4import cuchaz.enigma.mapping.*;
5import org.objectweb.asm.ClassVisitor;
6import org.objectweb.asm.MethodVisitor;
7
8public class IndexReferenceVisitor extends ClassVisitor {
9 private final JarIndex index;
10 private final ReferencedEntryPool entryPool;
11 private ClassEntry classEntry;
12
13 public IndexReferenceVisitor(JarIndex index, ReferencedEntryPool entryPool, int api) {
14 super(api);
15 this.index = index;
16 this.entryPool = entryPool;
17 }
18
19 @Override
20 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
21 this.classEntry = new ClassEntry(name);
22 }
23
24 @Override
25 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
26 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), new AccessFlags(access));
27 return new Method(this.index, entry, this.api);
28 }
29
30 private class Method extends MethodVisitor {
31 private final JarIndex index;
32 private final MethodDefEntry callerEntry;
33
34 public Method(JarIndex index, MethodDefEntry callerEntry, int api) {
35 super(api);
36 this.index = index;
37 this.callerEntry = callerEntry;
38 }
39
40 @Override
41 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
42 this.index.indexFieldAccess(callerEntry, owner, name, desc);
43 }
44
45 @Override
46 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
47 this.index.indexMethodCall(callerEntry, owner, name, desc);
48 }
49 }
50}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
deleted file mode 100644
index 87d3797..0000000
--- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java
+++ /dev/null
@@ -1,123 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.Constants;
16import cuchaz.enigma.mapping.ClassEntry;
17import javassist.ByteArrayClassPath;
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.Descriptor;
22
23import java.io.ByteArrayOutputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.Enumeration;
27import java.util.Iterator;
28import java.util.List;
29import java.util.jar.JarEntry;
30import java.util.jar.JarFile;
31
32public class JarClassIterator implements Iterator<CtClass> {
33
34 private JarFile jar;
35 private Iterator<JarEntry> iter;
36
37 public JarClassIterator(JarFile jar) {
38 this.jar = jar;
39
40 // get the jar entries that correspond to classes
41 List<JarEntry> classEntries = Lists.newArrayList();
42 Enumeration<JarEntry> entries = this.jar.entries();
43 while (entries.hasMoreElements()) {
44 JarEntry entry = entries.nextElement();
45
46 // is this a class file?
47 if (entry.getName().endsWith(".class")) {
48 classEntries.add(entry);
49 }
50 }
51 this.iter = classEntries.iterator();
52 }
53
54 public static List<ClassEntry> getClassEntries(JarFile jar) {
55 List<ClassEntry> classEntries = Lists.newArrayList();
56 Enumeration<JarEntry> entries = jar.entries();
57 while (entries.hasMoreElements()) {
58 JarEntry entry = entries.nextElement();
59
60 // is this a class file?
61 if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
62 classEntries.add(getClassEntry(entry));
63 }
64 }
65 return classEntries;
66 }
67
68 public static Iterable<CtClass> classes(final JarFile jar) {
69 return () -> new JarClassIterator(jar);
70 }
71
72 private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException {
73 // read the class into a buffer
74 ByteArrayOutputStream bos = new ByteArrayOutputStream();
75 byte[] buf = new byte[Constants.KiB];
76 int totalNumBytesRead = 0;
77 InputStream in = jar.getInputStream(entry);
78 while (in.available() > 0) {
79 int numBytesRead = in.read(buf);
80 if (numBytesRead < 0) {
81 break;
82 }
83 bos.write(buf, 0, numBytesRead);
84
85 // sanity checking
86 totalNumBytesRead += numBytesRead;
87 if (totalNumBytesRead > Constants.MiB) {
88 throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!");
89 }
90 }
91
92 // get a javassist handle for the class
93 String className = Descriptor.toJavaName(getClassEntry(entry).getName());
94 ClassPool classPool = new ClassPool();
95 classPool.appendSystemPath();
96 classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray()));
97 return classPool.get(className);
98 }
99
100 private static ClassEntry getClassEntry(JarEntry entry) {
101 return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length()));
102 }
103
104 @Override
105 public boolean hasNext() {
106 return this.iter.hasNext();
107 }
108
109 @Override
110 public CtClass next() {
111 JarEntry entry = this.iter.next();
112 try {
113 return getClass(this.jar, entry);
114 } catch (IOException | NotFoundException ex) {
115 throw new Error("Unable to load class: " + entry.getName());
116 }
117 }
118
119 @Override
120 public void remove() {
121 throw new UnsupportedOperationException();
122 }
123}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index d0d0f2c..972d4fe 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -12,113 +12,68 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.*; 14import com.google.common.collect.*;
15import cuchaz.enigma.bytecode.AccessFlags;
15import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.Translator; 17import org.objectweb.asm.Opcodes;
17import javassist.*;
18import javassist.bytecode.*;
19import javassist.expr.*;
20 18
21import java.lang.reflect.Modifier;
22import java.util.*; 19import java.util.*;
23import java.util.jar.JarFile;
24 20
25public class JarIndex { 21public class JarIndex {
26 22
23 private final ReferencedEntryPool entryPool;
24
27 private Set<ClassEntry> obfClassEntries; 25 private Set<ClassEntry> obfClassEntries;
28 private TranslationIndex translationIndex; 26 private TranslationIndex translationIndex;
29 private Map<Entry, Access> access; 27 private Map<Entry, Access> access;
30 private Multimap<ClassEntry, FieldEntry> fields; 28 private Multimap<ClassEntry, FieldDefEntry> fields;
31 private Multimap<ClassEntry, BehaviorEntry> behaviors; 29 private Multimap<ClassEntry, MethodDefEntry> methods;
32 private Multimap<String, MethodEntry> methodImplementations; 30 private Multimap<String, MethodDefEntry> methodImplementations;
33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; 31 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodReferences;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; 32 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 33 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
36 private Map<ClassEntry, ClassEntry> outerClassesByInner; 34 private Map<ClassEntry, ClassEntry> outerClassesByInner;
37 private Map<ClassEntry, BehaviorEntry> anonymousClasses;
38 private Map<MethodEntry, MethodEntry> bridgedMethods; 35 private Map<MethodEntry, MethodEntry> bridgedMethods;
39 private Set<MethodEntry> syntheticMethods; 36 private Set<MethodEntry> syntheticMethods;
40 37
41 public JarIndex() { 38 public JarIndex(ReferencedEntryPool entryPool) {
39 this.entryPool = entryPool;
42 this.obfClassEntries = Sets.newHashSet(); 40 this.obfClassEntries = Sets.newHashSet();
43 this.translationIndex = new TranslationIndex(); 41 this.translationIndex = new TranslationIndex(entryPool);
44 this.access = Maps.newHashMap(); 42 this.access = Maps.newHashMap();
45 this.fields = HashMultimap.create(); 43 this.fields = HashMultimap.create();
46 this.behaviors = HashMultimap.create(); 44 this.methods = HashMultimap.create();
47 this.methodImplementations = HashMultimap.create(); 45 this.methodImplementations = HashMultimap.create();
48 this.behaviorReferences = HashMultimap.create(); 46 this.methodReferences = HashMultimap.create();
49 this.fieldReferences = HashMultimap.create(); 47 this.fieldReferences = HashMultimap.create();
50 this.innerClassesByOuter = HashMultimap.create(); 48 this.innerClassesByOuter = HashMultimap.create();
51 this.outerClassesByInner = Maps.newHashMap(); 49 this.outerClassesByInner = Maps.newHashMap();
52 this.anonymousClasses = Maps.newHashMap();
53 this.bridgedMethods = Maps.newHashMap(); 50 this.bridgedMethods = Maps.newHashMap();
54 this.syntheticMethods = Sets.newHashSet(); 51 this.syntheticMethods = Sets.newHashSet();
55 } 52 }
56 53
57 public void indexJar(JarFile jar, boolean buildInnerClasses) { 54 public void indexJar(ParsedJar jar, boolean buildInnerClasses) {
58 55
59 // step 1: read the class names 56 // step 1: read the class names
60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); 57 obfClassEntries.addAll(jar.getClassEntries());
61
62 // step 2: index field/method/constructor access
63 for (CtClass c : JarClassIterator.classes(jar)) {
64 for (CtField field : c.getDeclaredFields()) {
65 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
66 this.access.put(fieldEntry, Access.get(field));
67 this.fields.put(fieldEntry.getClassEntry(), fieldEntry);
68 }
69 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
70 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
71 this.access.put(behaviorEntry, Access.get(behavior));
72 this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry);
73 }
74 }
75 58
76 // step 3: index extends, implements, fields, and methods 59 // step 2: index classes, fields, methods, interfaces
77 for (CtClass c : JarClassIterator.classes(jar)) { 60 jar.visit(node -> node.accept(new IndexClassVisitor(this, Opcodes.ASM5)));
78 this.translationIndex.indexClass(c);
79 String className = Descriptor.toJvmName(c.getName());
80 for (String interfaceName : c.getClassFile().getInterfaces()) {
81 className = Descriptor.toJvmName(className);
82 interfaceName = Descriptor.toJvmName(interfaceName);
83 if (className.equals(interfaceName)) {
84 throw new IllegalArgumentException("Class cannot be its own interface! " + className);
85 }
86 }
87 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
88 indexBehavior(behavior);
89 }
90 }
91 61
92 // step 4: index field, method, constructor references 62 // step 3: index field, method, constructor references
93 for (CtClass c : JarClassIterator.classes(jar)) { 63 jar.visit(node -> node.accept(new IndexReferenceVisitor(this, entryPool, Opcodes.ASM5)));
94 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 64
95 indexBehaviorReferences(behavior); 65 // step 4: index bridged methods
66 for (MethodDefEntry methodEntry : methods.values()) {
67 // look for bridge and bridged methods
68 MethodEntry bridgedMethod = findBridgedMethod(methodEntry);
69 if (bridgedMethod != null) {
70 this.bridgedMethods.put(methodEntry, bridgedMethod);
96 } 71 }
97 } 72 }
98 73
99 if (buildInnerClasses) { 74 if (buildInnerClasses) {
100
101 // step 5: index inner classes and anonymous classes 75 // step 5: index inner classes and anonymous classes
102 for (CtClass c : JarClassIterator.classes(jar)) { 76 jar.visit(node -> node.accept(new IndexInnerClassVisitor(this, Opcodes.ASM5)));
103 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c);
104 ClassEntry outerClassEntry = findOuterClass(c);
105 if (outerClassEntry != null) {
106 this.innerClassesByOuter.put(outerClassEntry, innerClassEntry);
107 boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null;
108 assert (innerWasAdded);
109
110 BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry);
111 if (enclosingBehavior != null) {
112 this.anonymousClasses.put(innerClassEntry, enclosingBehavior);
113
114 // DEBUG
115 //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
116 }/* else {
117 // DEBUG
118 //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName());
119 }*/
120 }
121 }
122 77
123 // step 6: update other indices with inner class info 78 // step 6: update other indices with inner class info
124 Map<String, String> renames = Maps.newHashMap(); 79 Map<String, String> renames = Maps.newHashMap();
@@ -133,385 +88,109 @@ public class JarIndex {
133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 88 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
134 this.translationIndex.renameClasses(renames); 89 this.translationIndex.renameClasses(renames);
135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 90 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); 91 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 92 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
138 EntryRenamer.renameClassesInMap(renames, this.access); 93 EntryRenamer.renameClassesInMap(renames, this.access);
139 } 94 }
140 } 95 }
141 96
142 private void indexBehavior(CtBehavior behavior) { 97 protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) {
143 // get the behavior entry 98 for (String interfaceName : interfaces) {
144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 99 if (name.equals(interfaceName)) {
145 if (behaviorEntry instanceof MethodEntry) { 100 throw new IllegalArgumentException("Class cannot be its own interface! " + name);
146 MethodEntry methodEntry = (MethodEntry) behaviorEntry;
147
148 // is synthetic
149 if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) {
150 syntheticMethods.add(methodEntry);
151 }
152
153 // index implementation
154 this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry);
155
156 // look for bridge and bridged methods
157 CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior);
158 if (bridgedMethod != null) {
159 this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod));
160 } 101 }
161 } 102 }
162 // looks like we don't care about constructors here 103 return this.translationIndex.indexClass(access, name, superName, interfaces);
163 }
164
165 private void indexBehaviorReferences(CtBehavior behavior) {
166 // index method calls
167 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
168 try {
169 behavior.instrument(new ExprEditor() {
170 @Override
171 public void edit(MethodCall call) {
172 MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call);
173 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry);
174 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
175 calledMethodEntry = new MethodEntry(
176 resolvedClassEntry,
177 calledMethodEntry.getName(),
178 calledMethodEntry.getSignature()
179 );
180 }
181 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
182 calledMethodEntry,
183 call.getMethodName(),
184 behaviorEntry
185 );
186 behaviorReferences.put(calledMethodEntry, reference);
187 }
188
189 @Override
190 public void edit(FieldAccess call) {
191 FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call);
192 ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry);
193 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
194 calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry);
195 }
196 EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>(
197 calledFieldEntry,
198 call.getFieldName(),
199 behaviorEntry
200 );
201 fieldReferences.put(calledFieldEntry, reference);
202 }
203
204 @Override
205 public void edit(ConstructorCall call) {
206 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
207 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
208 calledConstructorEntry,
209 call.getMethodName(),
210 behaviorEntry
211 );
212 behaviorReferences.put(calledConstructorEntry, reference);
213 }
214
215 @Override
216 public void edit(NewExpr call) {
217 ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call);
218 EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>(
219 calledConstructorEntry,
220 call.getClassName(),
221 behaviorEntry
222 );
223 behaviorReferences.put(calledConstructorEntry, reference);
224 }
225 });
226 } catch (CannotCompileException ex) {
227 throw new Error(ex);
228 }
229 } 104 }
230 105
231 private CtMethod getBridgedMethod(CtMethod method) { 106 protected void indexField(ClassDefEntry owner, int access, String name, String desc) {
232 107 FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), new AccessFlags(access));
233 // bridge methods just call another method, cast it to the return type, and return the result 108 this.translationIndex.indexField(fieldEntry);
234 // let's see if we can detect this scenario 109 this.access.put(fieldEntry, Access.get(access));
235 110 this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
236 // skip non-synthetic methods
237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
238 return null;
239 }
240
241 // get all the called methods
242 final List<MethodCall> methodCalls = Lists.newArrayList();
243 try {
244 method.instrument(new ExprEditor() {
245 @Override
246 public void edit(MethodCall call) {
247 methodCalls.add(call);
248 }
249 });
250 } catch (CannotCompileException ex) {
251 // this is stupid... we're not even compiling anything
252 throw new Error(ex);
253 }
254
255 // is there just one?
256 if (methodCalls.size() != 1) {
257 return null;
258 }
259 MethodCall call = methodCalls.get(0);
260
261 try {
262 // we have a bridge method!
263 return call.getMethod();
264 } catch (NotFoundException ex) {
265 // can't find the type? not a bridge method
266 return null;
267 }
268 } 111 }
269 112
270 private ClassEntry findOuterClass(CtClass c) { 113 protected void indexMethod(ClassDefEntry owner, int access, String name, String desc) {
271 114 MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), new AccessFlags(access));
272 ClassEntry classEntry = EntryFactory.getClassEntry(c); 115 this.translationIndex.indexMethod(methodEntry);
116 this.access.put(methodEntry, Access.get(access));
117 this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry);
273 118
274 // does this class already have an outer class? 119 if (new AccessFlags(access).isSynthetic()) {
275 if (classEntry.isInnerClass()) { 120 syntheticMethods.add(methodEntry);
276 return classEntry.getOuterClassEntry();
277 } 121 }
278 122
279 // inner classes: 123 // we don't care about constructors here
280 // have constructors that can (illegally) set synthetic fields 124 if (!methodEntry.isConstructor()) {
281 // the outer class is the only class that calls constructors 125 // index implementation
282 126 this.methodImplementations.put(methodEntry.getClassName(), methodEntry);
283 // use the synthetic fields to find the synthetic constructors
284 for (CtConstructor constructor : c.getDeclaredConstructors()) {
285 Set<String> syntheticFieldTypes = Sets.newHashSet();
286 if (!isIllegalConstructor(syntheticFieldTypes, constructor)) {
287 continue;
288 }
289
290 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
291
292 // gather the classes from the illegally-set synthetic fields
293 Set<ClassEntry> illegallySetClasses = Sets.newHashSet();
294 for (String type : syntheticFieldTypes) {
295 if (type.startsWith("L")) {
296 ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1));
297 if (isSaneOuterClass(outerClassEntry, classEntry)) {
298 illegallySetClasses.add(outerClassEntry);
299 }
300 }
301 }
302
303 // who calls this constructor?
304 Set<ClassEntry> callerClasses = Sets.newHashSet();
305 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) {
306
307 // make sure it's not a call to super
308 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
309
310 // is the entry a superclass of the context?
311 ClassEntry calledClassEntry = reference.entry.getClassEntry();
312 ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry());
313 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
314 // it's a super call, skip
315 continue;
316 }
317 }
318
319 if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) {
320 callerClasses.add(reference.context.getClassEntry());
321 }
322 }
323
324 // do we have an answer yet?
325 if (callerClasses.isEmpty()) {
326 if (illegallySetClasses.size() == 1) {
327 return illegallySetClasses.iterator().next();
328 } else {
329 System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry));
330 }
331 } else {
332 if (callerClasses.size() == 1) {
333 return callerClasses.iterator().next();
334 } else {
335 // multiple callers, do the illegally set classes narrow it down?
336 Set<ClassEntry> intersection = Sets.newHashSet(callerClasses);
337 intersection.retainAll(illegallySetClasses);
338 if (intersection.size() == 1) {
339 return intersection.iterator().next();
340 } else {
341 System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses));
342 }
343 }
344 }
345 } 127 }
346
347 return null;
348 } 128 }
349 129
350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { 130 protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) {
351 131 MethodEntry referencedMethod = new MethodEntry(entryPool.getClass(owner), name, new MethodDescriptor(desc));
352 // clearly this would be silly 132 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod);
353 if (outerClassEntry.equals(innerClassEntry)) { 133 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
354 return false; 134 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
355 } 135 }
356 136 methodReferences.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
357 // is the outer class in the jar?
358 return this.obfClassEntries.contains(outerClassEntry);
359
360 } 137 }
361 138
362 @SuppressWarnings("unchecked") 139 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { 140 FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc));
364 141 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField);
365 // illegal constructors only set synthetic member fields, then call super() 142 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) {
366 String className = constructor.getDeclaringClass().getName(); 143 referencedField = referencedField.updateOwnership(resolvedClassEntry);
367
368 // collect all the field accesses, constructor calls, and method calls
369 final List<FieldAccess> illegalFieldWrites = Lists.newArrayList();
370 final List<ConstructorCall> constructorCalls = Lists.newArrayList();
371 try {
372 constructor.instrument(new ExprEditor() {
373 @Override
374 public void edit(FieldAccess fieldAccess) {
375 if (fieldAccess.isWriter() && constructorCalls.isEmpty()) {
376 illegalFieldWrites.add(fieldAccess);
377 }
378 }
379
380 @Override
381 public void edit(ConstructorCall constructorCall) {
382 constructorCalls.add(constructorCall);
383 }
384 });
385 } catch (CannotCompileException ex) {
386 // we're not compiling anything... this is stupid
387 throw new Error(ex);
388 }
389
390 // are there any illegal field writes?
391 if (illegalFieldWrites.isEmpty()) {
392 return false;
393 }
394
395 // are all the writes to synthetic fields?
396 for (FieldAccess fieldWrite : illegalFieldWrites) {
397
398 // all illegal writes have to be to the local class
399 if (!fieldWrite.getClassName().equals(className)) {
400 System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName()));
401 return false;
402 }
403
404 // find the field
405 FieldInfo fieldInfo = null;
406 for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) {
407 if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) {
408 fieldInfo = info;
409 break;
410 }
411 }
412 if (fieldInfo == null) {
413 // field is in a superclass or something, can't be a local synthetic member
414 return false;
415 }
416
417 // is this field synthetic?
418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
419 if (isSynthetic) {
420 syntheticFieldTypes.add(fieldInfo.getDescriptor());
421 } else {
422 System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName()));
423 return false;
424 }
425 } 144 }
426 145 fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry));
427 // we passed all the tests!
428 return true;
429 } 146 }
430 147
431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { 148 public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) {
432 149 this.innerClassesByOuter.put(outerEntry, innerEntry);
433 // is this class already marked anonymous? 150 boolean innerWasAdded = this.outerClassesByInner.put(innerEntry, outerEntry) == null;
434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 151 assert (innerWasAdded);
435 if (enclosingMethodAttribute != null) { 152 }
436 if (enclosingMethodAttribute.methodIndex() > 0) {
437 return EntryFactory.getBehaviorEntry(
438 Descriptor.toJvmName(enclosingMethodAttribute.className()),
439 enclosingMethodAttribute.methodName(),
440 enclosingMethodAttribute.methodDescriptor()
441 );
442 } else {
443 // an attribute but no method? assume not anonymous
444 return null;
445 }
446 }
447
448 // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous
449 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
450 if (innerClassesAttribute != null) {
451 return null;
452 }
453 153
454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 154 private MethodEntry findBridgedMethod(MethodDefEntry method) {
455 155
456 // anonymous classes: 156 // bridge methods just call another method, cast it to the return desc, and return the result
457 // can't be abstract 157 // let's see if we can detect this scenario
458 // have only one constructor
459 // it's called exactly once by the outer class
460 // the type the instance is assigned to can't be this type
461 158
462 // is abstract? 159 // skip non-synthetic methods
463 if (Modifier.isAbstract(c.getModifiers())) { 160 if (!method.getAccess().isSynthetic()) {
464 return null; 161 return null;
465 } 162 }
466 163
467 // is there exactly one constructor? 164 // get all the called methods
468 if (c.getDeclaredConstructors().length != 1) { 165 final Collection<EntryReference<MethodEntry, MethodDefEntry>> referencedMethods = methodReferences.get(method);
469 return null;
470 }
471 CtConstructor constructor = c.getDeclaredConstructors()[0];
472 166
473 // is this constructor called exactly once? 167 // is there just one?
474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 168 if (referencedMethods.size() != 1) {
475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
476 if (references.size() != 1) {
477 return null; 169 return null;
478 } 170 }
479 171
480 // does the caller use this type? 172 // we have a bridge method!
481 BehaviorEntry caller = references.iterator().next().context; 173 return referencedMethods.stream().findFirst().get().entry;
482 for (FieldEntry fieldEntry : getReferencedFields(caller)) {
483 if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) {
484 // caller references this type, so it can't be anonymous
485 return null;
486 }
487 }
488 for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) {
489 if (behaviorEntry.getSignature().hasClass(innerClassEntry)) {
490 return null;
491 }
492 }
493
494 return caller;
495 } 174 }
496 175
497 public Set<ClassEntry> getObfClassEntries() { 176 public Set<ClassEntry> getObfClassEntries() {
498 return this.obfClassEntries; 177 return this.obfClassEntries;
499 } 178 }
500 179
501 public Collection<FieldEntry> getObfFieldEntries() { 180 public Collection<FieldDefEntry> getObfFieldEntries() {
502 return this.fields.values(); 181 return this.fields.values();
503 } 182 }
504 183
505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { 184 public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) {
506 return this.fields.get(classEntry); 185 return this.fields.get(classEntry);
507 } 186 }
508 187
509 public Collection<BehaviorEntry> getObfBehaviorEntries() { 188 public Collection<MethodDefEntry> getObfBehaviorEntries() {
510 return this.behaviors.values(); 189 return this.methods.values();
511 } 190 }
512 191
513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { 192 public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) {
514 return this.behaviors.get(classEntry); 193 return this.methods.get(classEntry);
515 } 194 }
516 195
517 public TranslationIndex getTranslationIndex() { 196 public TranslationIndex getTranslationIndex() {
@@ -533,8 +212,8 @@ public class JarIndex {
533 } 212 }
534 } 213 }
535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 214 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
536 deobfuscatingTranslator, 215 deobfuscatingTranslator,
537 ancestry.get(ancestry.size() - 1) 216 ancestry.get(ancestry.size() - 1)
538 ); 217 );
539 218
540 // expand all children recursively 219 // expand all children recursively
@@ -557,28 +236,20 @@ public class JarIndex {
557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 236 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
558 237
559 // travel to the ancestor implementation 238 // travel to the ancestor implementation
560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); 239 ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry();
561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { 240 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getOwnerClassEntry())) {
562 MethodEntry ancestorMethodEntry = new MethodEntry( 241 MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
563 new ClassEntry(ancestorClassEntry), 242 if (ancestorMethodEntry != null && containsObfMethod(ancestorMethodEntry)) {
564 obfMethodEntry.getName(),
565 obfMethodEntry.getSignature()
566 );
567 if (containsObfBehavior(ancestorMethodEntry)) {
568 baseImplementationClassEntry = ancestorClassEntry; 243 baseImplementationClassEntry = ancestorClassEntry;
569 } 244 }
570 } 245 }
571 246
572 // make a root node at the base 247 // make a root node at the base
573 MethodEntry methodEntry = new MethodEntry( 248 MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
574 baseImplementationClassEntry,
575 obfMethodEntry.getName(),
576 obfMethodEntry.getSignature()
577 );
578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 249 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
579 deobfuscatingTranslator, 250 deobfuscatingTranslator,
580 methodEntry, 251 methodEntry,
581 containsObfBehavior(methodEntry) 252 containsObfMethod(methodEntry)
582 ); 253 );
583 254
584 // expand the full tree 255 // expand the full tree
@@ -599,12 +270,8 @@ public class JarIndex {
599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { 270 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
600 271
601 // is this method defined in this interface? 272 // is this method defined in this interface?
602 MethodEntry methodInterface = new MethodEntry( 273 MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
603 interfaceEntry, 274 if (methodInterface != null && containsObfMethod(methodInterface)) {
604 obfMethodEntry.getName(),
605 obfMethodEntry.getSignature()
606 );
607 if (containsObfBehavior(methodInterface)) {
608 interfaceMethodEntries.add(methodInterface); 275 interfaceMethodEntries.add(methodInterface);
609 } 276 }
610 } 277 }
@@ -623,14 +290,14 @@ public class JarIndex {
623 290
624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { 291 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
625 Set<MethodEntry> methodEntries = Sets.newHashSet(); 292 Set<MethodEntry> methodEntries = Sets.newHashSet();
626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); 293 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry));
627 return methodEntries; 294 return methodEntries;
628 } 295 }
629 296
630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 297 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
631 MethodEntry methodEntry = node.getMethodEntry(); 298 MethodEntry methodEntry = node.getMethodEntry();
632 299
633 if (containsObfBehavior(methodEntry)) { 300 if (containsObfMethod(methodEntry)) {
634 // collect the entry 301 // collect the entry
635 methodEntries.add(methodEntry); 302 methodEntries.add(methodEntry);
636 } 303 }
@@ -643,7 +310,7 @@ public class JarIndex {
643 } 310 }
644 311
645 // look at interface methods too 312 // look at interface methods too
646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { 313 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) {
647 getRelatedMethodImplementations(methodEntries, implementationsNode); 314 getRelatedMethodImplementations(methodEntries, implementationsNode);
648 } 315 }
649 316
@@ -655,7 +322,7 @@ public class JarIndex {
655 322
656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 323 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
657 MethodEntry methodEntry = node.getMethodEntry(); 324 MethodEntry methodEntry = node.getMethodEntry();
658 if (containsObfBehavior(methodEntry)) { 325 if (containsObfMethod(methodEntry)) {
659 // collect the entry 326 // collect the entry
660 methodEntries.add(methodEntry); 327 methodEntries.add(methodEntry);
661 } 328 }
@@ -673,30 +340,30 @@ public class JarIndex {
673 } 340 }
674 } 341 }
675 342
676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { 343 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) {
677 return this.fieldReferences.get(fieldEntry); 344 return this.fieldReferences.get(fieldEntry);
678 } 345 }
679 346
680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { 347 public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) {
681 // linear search is fast enough for now 348 // linear search is fast enough for now
682 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 349 Set<FieldEntry> fieldEntries = Sets.newHashSet();
683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { 350 for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) {
684 if (reference.context == behaviorEntry) { 351 if (reference.context == methodEntry) {
685 fieldEntries.add(reference.entry); 352 fieldEntries.add(reference.entry);
686 } 353 }
687 } 354 }
688 return fieldEntries; 355 return fieldEntries;
689 } 356 }
690 357
691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { 358 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodReferences(MethodEntry methodEntry) {
692 return this.behaviorReferences.get(behaviorEntry); 359 return this.methodReferences.get(methodEntry);
693 } 360 }
694 361
695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { 362 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
696 // linear search is fast enough for now 363 // linear search is fast enough for now
697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); 364 Set<MethodEntry> behaviorEntries = Sets.newHashSet();
698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { 365 for (EntryReference<MethodEntry, MethodDefEntry> reference : this.methodReferences.values()) {
699 if (reference.context == behaviorEntry) { 366 if (reference.context == methodEntry) {
700 behaviorEntries.add(reference.entry); 367 behaviorEntries.add(reference.entry);
701 } 368 }
702 } 369 }
@@ -711,20 +378,12 @@ public class JarIndex {
711 return this.outerClassesByInner.get(obfInnerClassEntry); 378 return this.outerClassesByInner.get(obfInnerClassEntry);
712 } 379 }
713 380
714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) {
715 return this.anonymousClasses.containsKey(obfInnerClassEntry);
716 }
717
718 public boolean isSyntheticMethod(MethodEntry methodEntry) { 381 public boolean isSyntheticMethod(MethodEntry methodEntry) {
719 return this.syntheticMethods.contains(methodEntry); 382 return this.syntheticMethods.contains(methodEntry);
720 } 383 }
721 384
722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) {
723 return this.anonymousClasses.get(obfInnerClassName);
724 }
725
726 public Set<ClassEntry> getInterfaces(String className) { 385 public Set<ClassEntry> getInterfaces(String className) {
727 ClassEntry classEntry = new ClassEntry(className); 386 ClassEntry classEntry = entryPool.getClass(className);
728 Set<ClassEntry> interfaces = new HashSet<>(); 387 Set<ClassEntry> interfaces = new HashSet<>();
729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); 388 interfaces.addAll(this.translationIndex.getInterfaces(classEntry));
730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { 389 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
@@ -754,7 +413,7 @@ public class JarIndex {
754 } 413 }
755 414
756 public boolean isInterface(String className) { 415 public boolean isInterface(String className) {
757 return this.translationIndex.isInterface(new ClassEntry(className)); 416 return this.translationIndex.isInterface(entryPool.getClass(className));
758 } 417 }
759 418
760 public boolean containsObfClass(ClassEntry obfClassEntry) { 419 public boolean containsObfClass(ClassEntry obfClassEntry) {
@@ -765,8 +424,8 @@ public class JarIndex {
765 return this.access.containsKey(obfFieldEntry); 424 return this.access.containsKey(obfFieldEntry);
766 } 425 }
767 426
768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { 427 public boolean containsObfMethod(MethodEntry obfMethodEntry) {
769 return this.access.containsKey(obfBehaviorEntry); 428 return this.access.containsKey(obfMethodEntry);
770 } 429 }
771 430
772 public boolean containsEntryWithSameName(Entry entry) { 431 public boolean containsEntryWithSameName(Entry entry) {
@@ -776,15 +435,13 @@ public class JarIndex {
776 return false; 435 return false;
777 } 436 }
778 437
779 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 438 public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) {
780 // check the behavior 439 // check the behavior
781 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 440 if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) {
782 return false; 441 return false;
783 } 442 }
784 443
785 // check the argument 444 return true;
786 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size();
787
788 } 445 }
789 446
790 public boolean containsObfEntry(Entry obfEntry) { 447 public boolean containsObfEntry(Entry obfEntry) {
@@ -792,15 +449,12 @@ public class JarIndex {
792 return containsObfClass((ClassEntry) obfEntry); 449 return containsObfClass((ClassEntry) obfEntry);
793 } else if (obfEntry instanceof FieldEntry) { 450 } else if (obfEntry instanceof FieldEntry) {
794 return containsObfField((FieldEntry) obfEntry); 451 return containsObfField((FieldEntry) obfEntry);
795 } else if (obfEntry instanceof BehaviorEntry) { 452 } else if (obfEntry instanceof MethodEntry) {
796 return containsObfBehavior((BehaviorEntry) obfEntry); 453 return containsObfMethod((MethodEntry) obfEntry);
797 } else if (obfEntry instanceof ArgumentEntry) {
798 return containsObfArgument((ArgumentEntry) obfEntry);
799 } else if (obfEntry instanceof LocalVariableEntry) { 454 } else if (obfEntry instanceof LocalVariableEntry) {
800 // TODO: Implement it 455 return containsObfVariable((LocalVariableEntry) obfEntry);
801 return false;
802 } else { 456 } else {
803 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 457 throw new Error("Entry desc not supported: " + obfEntry.getClass().getName());
804 } 458 }
805 } 459 }
806 460
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index bacb1aa..6791b83 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -54,11 +54,11 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
54 } 54 }
55 55
56 public String getDeobfClassName() { 56 public String getDeobfClassName() {
57 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 57 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName();
58 } 58 }
59 59
60 public String getDeobfMethodName() { 60 public String getDeobfMethodName() {
61 return this.deobfuscatingTranslator.translate(this.entry); 61 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 } 62 }
63 63
64 @Override 64 @Override
@@ -80,9 +80,8 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
80 // get all method implementations 80 // get all method implementations
81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 81 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 82 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) {
83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() 83 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc());
84 ); 84 if (index.containsObfMethod(methodEntry)) {
85 if (index.containsObfBehavior(methodEntry)) {
86 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 85 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry));
87 } 86 }
88 } 87 }
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index 4f84dd0..f060ed9 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -52,11 +52,11 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
52 } 52 }
53 53
54 public String getDeobfClassName() { 54 public String getDeobfClassName() {
55 return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); 55 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName();
56 } 56 }
57 57
58 public String getDeobfMethodName() { 58 public String getDeobfMethodName() {
59 return this.deobfuscatingTranslator.translate(this.entry); 59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
60 } 60 }
61 61
62 public boolean isImplemented() { 62 public boolean isImplemented() {
@@ -84,11 +84,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
84 public void load(JarIndex index, boolean recurse) { 84 public void load(JarIndex index, boolean recurse) {
85 // get all the child nodes 85 // get all the child nodes
86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { 87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) {
88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() 88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc());
89 ); 89 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry)));
90 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry)
91 ));
92 } 90 }
93 91
94 // add them to this node 92 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index 6556b2c..37b4073 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -12,30 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.mapping.BehaviorEntry; 15import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.Entry;
17import cuchaz.enigma.mapping.Translator;
18 16
19import javax.swing.tree.DefaultMutableTreeNode; 17import javax.swing.tree.DefaultMutableTreeNode;
20import javax.swing.tree.TreeNode; 18import javax.swing.tree.TreeNode;
21import java.util.Set; 19import java.util.Set;
22 20
23public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode 21public class MethodReferenceTreeNode extends DefaultMutableTreeNode
24 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> { 22 implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
25 23
26 private Translator deobfuscatingTranslator; 24 private Translator deobfuscatingTranslator;
27 private BehaviorEntry entry; 25 private MethodEntry entry;
28 private EntryReference<BehaviorEntry, BehaviorEntry> reference; 26 private EntryReference<MethodEntry, MethodDefEntry> reference;
29 private Access access; 27 private Access access;
30 28
31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { 29 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) {
32 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.entry = entry; 31 this.entry = entry;
34 this.reference = null; 32 this.reference = null;
35 } 33 }
36 34
37 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, 35 public MethodReferenceTreeNode(Translator deobfuscatingTranslator,
38 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) { 36 EntryReference<MethodEntry, MethodDefEntry> reference, Access access) {
39 this.deobfuscatingTranslator = deobfuscatingTranslator; 37 this.deobfuscatingTranslator = deobfuscatingTranslator;
40 this.entry = reference.entry; 38 this.entry = reference.entry;
41 this.reference = reference; 39 this.reference = reference;
@@ -43,42 +41,42 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode
43 } 41 }
44 42
45 @Override 43 @Override
46 public BehaviorEntry getEntry() { 44 public MethodEntry getEntry() {
47 return this.entry; 45 return this.entry;
48 } 46 }
49 47
50 @Override 48 @Override
51 public EntryReference<BehaviorEntry, BehaviorEntry> getReference() { 49 public EntryReference<MethodEntry, MethodDefEntry> getReference() {
52 return this.reference; 50 return this.reference;
53 } 51 }
54 52
55 @Override 53 @Override
56 public String toString() { 54 public String toString() {
57 if (this.reference != null) { 55 if (this.reference != null) {
58 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), 56 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context),
59 this.access); 57 this.access);
60 } 58 }
61 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 } 60 }
63 61
64 public void load(JarIndex index, boolean recurse) { 62 public void load(JarIndex index, boolean recurse) {
65 // get all the child nodes 63 // get all the child nodes
66 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) { 64 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodReferences(this.entry)) {
67 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 65 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
68 } 66 }
69 67
70 if (recurse && this.children != null) { 68 if (recurse && this.children != null) {
71 for (Object child : this.children) { 69 for (Object child : this.children) {
72 if (child instanceof BehaviorReferenceTreeNode) { 70 if (child instanceof MethodReferenceTreeNode) {
73 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; 71 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child;
74 72
75 // don't recurse into ancestor 73 // don't recurse into ancestor
76 Set<Entry> ancestors = Sets.newHashSet(); 74 Set<Entry> ancestors = Sets.newHashSet();
77 TreeNode n = node; 75 TreeNode n = node;
78 while (n.getParent() != null) { 76 while (n.getParent() != null) {
79 n = n.getParent(); 77 n = n.getParent();
80 if (n instanceof BehaviorReferenceTreeNode) { 78 if (n instanceof MethodReferenceTreeNode) {
81 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); 79 ancestors.add(((MethodReferenceTreeNode) n).getEntry());
82 } 80 }
83 } 81 }
84 if (ancestors.contains(node.getEntry())) { 82 if (ancestors.contains(node.getEntry())) {
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
new file mode 100644
index 0000000..78ef722
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -0,0 +1,72 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import cuchaz.enigma.mapping.ClassEntry;
15import org.objectweb.asm.ClassReader;
16import org.objectweb.asm.tree.ClassNode;
17
18import java.io.IOException;
19import java.io.InputStream;
20import java.util.*;
21import java.util.function.Consumer;
22import java.util.jar.JarEntry;
23import java.util.jar.JarFile;
24
25public class ParsedJar {
26 private final Map<String, ClassNode> nodes = new LinkedHashMap<>();
27
28 public ParsedJar(JarFile jar) throws IOException {
29 try {
30 // get the jar entries that correspond to classes
31 Enumeration<JarEntry> entries = jar.entries();
32 while (entries.hasMoreElements()) {
33 JarEntry entry = entries.nextElement();
34 // is this a class file?
35 if (entry.getName().endsWith(".class")) {
36 try (InputStream input = jar.getInputStream(entry)) {
37 // read the ClassNode from the jar
38 ClassReader reader = new ClassReader(input);
39 ClassNode node = new ClassNode();
40 reader.accept(node, 0);
41 String path = entry.getName().substring(0, entry.getName().length() - ".class".length());
42 nodes.put(path, node);
43 }
44 }
45 }
46 } finally {
47 jar.close();
48 }
49 }
50
51 public void visit(Consumer<ClassNode> visitor) {
52 for (ClassNode node : nodes.values()) {
53 visitor.accept(node);
54 }
55 }
56
57 public int getClassCount() {
58 return nodes.size();
59 }
60
61 public List<ClassEntry> getClassEntries() {
62 List<ClassEntry> entries = new ArrayList<>(nodes.size());
63 for (ClassNode node : nodes.values()) {
64 entries.add(new ClassEntry(node.name));
65 }
66 return entries;
67 }
68
69 public ClassNode getClassNode(String name) {
70 return nodes.get(name);
71 }
72}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index b13415d..015eaac 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -20,11 +20,16 @@ import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.mapping.*; 20import cuchaz.enigma.mapping.*;
21 21
22public class SourceIndexClassVisitor extends SourceIndexVisitor { 22public class SourceIndexClassVisitor extends SourceIndexVisitor {
23 private final ReferencedEntryPool entryPool;
24 private final ProcyonEntryFactory entryFactory;
23 25
24 private ClassEntry classEntry; 26 private ClassEntry classEntry;
25 private boolean isEnum; 27 private boolean isEnum;
26 28
27 public SourceIndexClassVisitor(ClassEntry classEntry) { 29 public SourceIndexClassVisitor(ReferencedEntryPool entryPool, ClassEntry classEntry) {
30 super(entryPool);
31 this.entryPool = entryPool;
32 this.entryFactory = new ProcyonEntryFactory(entryPool);
28 this.classEntry = classEntry; 33 this.classEntry = classEntry;
29 } 34 }
30 35
@@ -34,9 +39,9 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 39 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
35 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 40 ClassEntry classEntry = new ClassEntry(def.getInternalName());
36 if (!classEntry.equals(this.classEntry)) { 41 if (!classEntry.equals(this.classEntry)) {
37 // it's a sub-type, recurse 42 // it's a subtype, recurse
38 index.addDeclaration(node.getNameToken(), classEntry); 43 index.addDeclaration(node.getNameToken(), classEntry);
39 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 44 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
40 } 45 }
41 46
42 return recurse(node, index); 47 return recurse(node, index);
@@ -56,31 +61,28 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
56 @Override 61 @Override
57 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 62 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 63 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
59 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); 64 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
60 AstNode tokenNode = node.getNameToken(); 65 AstNode tokenNode = node.getNameToken();
61 if (behaviorEntry instanceof ConstructorEntry) { 66 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
62 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; 67 // for static initializers, check elsewhere for the token node
63 if (constructorEntry.isStatic()) { 68 tokenNode = node.getModifiers().firstOrNullObject();
64 // for static initializers, check elsewhere for the token node
65 tokenNode = node.getModifiers().firstOrNullObject();
66 }
67 } 69 }
68 index.addDeclaration(tokenNode, behaviorEntry); 70 index.addDeclaration(tokenNode, methodEntry);
69 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry, false), index); 71 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry, false), index);
70 } 72 }
71 73
72 @Override 74 @Override
73 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 75 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
74 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 76 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
75 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); 77 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
76 index.addDeclaration(node.getNameToken(), constructorEntry); 78 index.addDeclaration(node.getNameToken(), methodEntry);
77 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry, isEnum), index); 79 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry, isEnum), index);
78 } 80 }
79 81
80 @Override 82 @Override
81 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 83 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
82 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 84 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
83 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 85 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
84 assert (node.getVariables().size() == 1); 86 assert (node.getVariables().size() == 1);
85 VariableInitializer variable = node.getVariables().firstOrNullObject(); 87 VariableInitializer variable = node.getVariables().firstOrNullObject();
86 index.addDeclaration(variable.getNameToken(), fieldEntry); 88 index.addDeclaration(variable.getNameToken(), fieldEntry);
@@ -92,7 +94,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
92 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 94 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
93 // treat enum declarations as field declarations 95 // treat enum declarations as field declarations
94 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 96 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
95 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 97 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
96 index.addDeclaration(node.getNameToken(), fieldEntry); 98 index.addDeclaration(node.getNameToken(), fieldEntry);
97 this.isEnum = true; 99 this.isEnum = true;
98 return recurse(node, index); 100 return recurse(node, index);
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 1b61916..2a62241 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -20,13 +20,15 @@ import com.strobel.assembler.metadata.TypeReference;
20import com.strobel.decompiler.languages.TextLocation; 20import com.strobel.decompiler.languages.TextLocation;
21import com.strobel.decompiler.languages.java.ast.*; 21import com.strobel.decompiler.languages.java.ast.*;
22import cuchaz.enigma.mapping.*; 22import cuchaz.enigma.mapping.*;
23import javassist.bytecode.Descriptor;
24 23
25import java.util.HashMap; 24import java.util.HashMap;
26import java.util.Map; 25import java.util.Map;
27 26
28public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { 27public class SourceIndexMethodVisitor extends SourceIndexVisitor {
29 private BehaviorEntry behaviorEntry; 28 private final ReferencedEntryPool entryPool;
29 private final ProcyonEntryFactory entryFactory;
30
31 private MethodDefEntry methodEntry;
30 32
31 // TODO: Really fix Procyon index problem with inner classes 33 // TODO: Really fix Procyon index problem with inner classes
32 private int argumentPosition; 34 private int argumentPosition;
@@ -34,8 +36,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 36 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 37 private Map<String, Entry> identifierEntryCache = new HashMap<>();
36 38
37 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry, boolean isEnum) { 39 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry, boolean isEnum) {
38 this.behaviorEntry = behaviorEntry; 40 super(entryPool);
41 this.entryPool = entryPool;
42 this.entryFactory = new ProcyonEntryFactory(entryPool);
43 this.methodEntry = methodEntry;
39 this.argumentPosition = isEnum ? 2 : 0; 44 this.argumentPosition = isEnum ? 2 : 0;
40 this.localsPosition = 0; 45 this.localsPosition = 0;
41 } 46 }
@@ -45,19 +50,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
45 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 50 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
46 51
47 // get the behavior entry 52 // get the behavior entry
48 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 53 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
49 BehaviorEntry behaviorEntry = null; 54 MethodEntry methodEntry = null;
50 if (ref instanceof MethodReference) { 55 if (ref instanceof MethodReference) {
51 MethodReference methodRef = (MethodReference) ref; 56 methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature());
52 if (methodRef.isConstructor()) {
53 behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature()));
54 } else if (methodRef.isTypeInitializer()) {
55 behaviorEntry = new ConstructorEntry(classEntry);
56 } else {
57 behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature()));
58 }
59 } 57 }
60 if (behaviorEntry != null) { 58 if (methodEntry != null) {
61 // get the node for the token 59 // get the node for the token
62 AstNode tokenNode = null; 60 AstNode tokenNode = null;
63 if (node.getTarget() instanceof MemberReferenceExpression) { 61 if (node.getTarget() instanceof MemberReferenceExpression) {
@@ -68,13 +66,13 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
68 tokenNode = node.getTarget(); 66 tokenNode = node.getTarget();
69 } 67 }
70 if (tokenNode != null) { 68 if (tokenNode != null) {
71 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); 69 index.addReference(tokenNode, methodEntry, this.methodEntry);
72 } 70 }
73 } 71 }
74 72
75 // Check for identifier 73 // Check for identifier
76 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 74 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
77 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); 75 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
78 return recurse(node, index); 76 return recurse(node, index);
79 } 77 }
80 78
@@ -83,13 +81,17 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
83 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 81 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
84 if (ref != null) { 82 if (ref != null) {
85 // make sure this is actually a field 83 // make sure this is actually a field
86 if (ref.getErasedSignature().indexOf('(') >= 0) { 84 String erasedSignature = ref.getErasedSignature();
85 if (erasedSignature.indexOf('(') >= 0) {
87 throw new Error("Expected a field here! got " + ref); 86 throw new Error("Expected a field here! got " + ref);
88 } 87 }
89 88
90 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 89 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
91 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 90 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
92 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); 91 if (fieldEntry == null) {
92 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName());
93 }
94 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
93 } 95 }
94 96
95 return recurse(node, index); 97 return recurse(node, index);
@@ -99,8 +101,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
99 public Void visitSimpleType(SimpleType node, SourceIndex index) { 101 public Void visitSimpleType(SimpleType node, SourceIndex index) {
100 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 102 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
101 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 103 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
102 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 104 ClassEntry classEntry = entryPool.getClass(ref.getInternalName());
103 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); 105 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry);
104 } 106 }
105 107
106 return recurse(node, index); 108 return recurse(node, index);
@@ -110,12 +112,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
110 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 112 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
111 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 113 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
112 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) { 114 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) {
113 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), 115 MethodEntry methodEntry = entryFactory.getMethodEntry((MethodReference) def.getMethod());
114 argumentPosition++, node.getName()); 116 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition++, node.getName());
115 Identifier identifier = node.getNameToken(); 117 Identifier identifier = node.getNameToken();
116 // cache the argument entry and the identifier 118 // cache the argument entry and the identifier
117 identifierEntryCache.put(identifier.getName(), argumentEntry); 119 identifierEntryCache.put(identifier.getName(), localVariableEntry);
118 index.addDeclaration(identifier, argumentEntry); 120 index.addDeclaration(identifier, localVariableEntry);
119 } 121 }
120 122
121 return recurse(node, index); 123 return recurse(node, index);
@@ -125,9 +127,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
125 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 127 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
126 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 128 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
127 if (ref != null) { 129 if (ref != null) {
128 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 130 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
129 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 131 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
130 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); 132 if (fieldEntry == null) {
133 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName());
134 }
135 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
131 } else 136 } else
132 this.checkIdentifier(node, index); 137 this.checkIdentifier(node, index);
133 return recurse(node, index); 138 return recurse(node, index);
@@ -155,11 +160,11 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
155 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 160 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 161 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
157 if (ref != null) { 162 if (ref != null) {
158 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 163 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
159 ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); 164 MethodEntry constructorEntry = entryPool.getMethod(classEntry, "<init>", ref.getErasedSignature());
160 if (node.getType() instanceof SimpleType) { 165 if (node.getType() instanceof SimpleType) {
161 SimpleType simpleTypeNode = (SimpleType) node.getType(); 166 SimpleType simpleTypeNode = (SimpleType) node.getType();
162 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); 167 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry);
163 } 168 }
164 } 169 }
165 170
@@ -169,11 +174,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
169 @Override 174 @Override
170 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { 175 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
171 if (node.getVariableType() instanceof SimpleType) { 176 if (node.getVariableType() instanceof SimpleType) {
172 SimpleType type = (SimpleType) node.getVariableType();
173 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
174 Identifier identifier = node.getVariableNameToken(); 177 Identifier identifier = node.getVariableNameToken();
175 String signature = Descriptor.of(typeReference.getErasedDescription()); 178 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition + localsPosition++, identifier.getName());
176 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, identifier.getName(), new Type(signature));
177 identifierEntryCache.put(identifier.getName(), localVariableEntry); 179 identifierEntryCache.put(identifier.getName(), localVariableEntry);
178 addDeclarationToUnmatched(identifier.getName(), index); 180 addDeclarationToUnmatched(identifier.getName(), index);
179 index.addDeclaration(identifier, localVariableEntry); 181 index.addDeclaration(identifier, localVariableEntry);
@@ -189,11 +191,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
189 if (variables.size() == 1) { 191 if (variables.size() == 1) {
190 VariableInitializer initializer = variables.firstOrNullObject(); 192 VariableInitializer initializer = variables.firstOrNullObject();
191 if (initializer != null && node.getType() instanceof SimpleType) { 193 if (initializer != null && node.getType() instanceof SimpleType) {
192 SimpleType type = (SimpleType) node.getType();
193 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
194 String signature = Descriptor.of(typeReference.getErasedDescription());
195 Identifier identifier = initializer.getNameToken(); 194 Identifier identifier = initializer.getNameToken();
196 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, initializer.getName(), new Type(signature)); 195 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, argumentPosition + localsPosition++, initializer.getName());
197 identifierEntryCache.put(identifier.getName(), localVariableEntry); 196 identifierEntryCache.put(identifier.getName(), localVariableEntry);
198 addDeclarationToUnmatched(identifier.getName(), index); 197 addDeclarationToUnmatched(identifier.getName(), index);
199 index.addDeclaration(identifier, localVariableEntry); 198 index.addDeclaration(identifier, localVariableEntry);
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index a94a55b..241b9f7 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -15,8 +15,14 @@ import com.strobel.assembler.metadata.TypeDefinition;
15import com.strobel.decompiler.languages.java.ast.*; 15import com.strobel.decompiler.languages.java.ast.*;
16import com.strobel.decompiler.patterns.Pattern; 16import com.strobel.decompiler.patterns.Pattern;
17import cuchaz.enigma.mapping.ClassEntry; 17import cuchaz.enigma.mapping.ClassEntry;
18import cuchaz.enigma.mapping.ReferencedEntryPool;
18 19
19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 20public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
21 private final ReferencedEntryPool entryPool;
22
23 public SourceIndexVisitor(ReferencedEntryPool entryPool) {
24 this.entryPool = entryPool;
25 }
20 26
21 @Override 27 @Override
22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 28 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
@@ -24,7 +30,7 @@ public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
24 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 30 ClassEntry classEntry = new ClassEntry(def.getInternalName());
25 index.addDeclaration(node.getNameToken(), classEntry); 31 index.addDeclaration(node.getNameToken(), classEntry);
26 32
27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 33 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
28 } 34 }
29 35
30 protected Void recurse(AstNode node, SourceIndex index) { 36 protected Void recurse(AstNode node, SourceIndex index) {
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index 26be05b..3cd80ff 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -15,11 +15,8 @@ import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
18import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.*; 19import cuchaz.enigma.mapping.*;
19import javassist.CtBehavior;
20import javassist.CtClass;
21import javassist.CtField;
22import javassist.bytecode.Descriptor;
23 20
24import java.util.Collection; 21import java.util.Collection;
25import java.util.List; 22import java.util.List;
@@ -28,96 +25,91 @@ import java.util.Set;
28 25
29public class TranslationIndex { 26public class TranslationIndex {
30 27
28 private final ReferencedEntryPool entryPool;
31 private Map<ClassEntry, ClassEntry> superclasses; 29 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 30 private Multimap<ClassEntry, FieldDefEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 31 private Multimap<ClassEntry, MethodDefEntry> methodEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 32 private Multimap<ClassEntry, ClassEntry> interfaces;
35 33
36 public TranslationIndex() { 34 public TranslationIndex(ReferencedEntryPool entryPool) {
35 this.entryPool = entryPool;
37 this.superclasses = Maps.newHashMap(); 36 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 37 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 38 this.methodEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 39 this.interfaces = HashMultimap.create();
41 } 40 }
42 41
43 public TranslationIndex(TranslationIndex other, Translator translator) { 42 public TranslationIndex(TranslationIndex other, Translator translator) {
43 this.entryPool = other.entryPool;
44
44 // translate the superclasses 45 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 46 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 47 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 48 this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue()));
48 } 49 }
49 50
50 // translate the interfaces 51 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 52 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 53 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 54 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 55 translator.getTranslatedClass(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 56 translator.getTranslatedClass(mapEntry.getValue())
56 ); 57 );
57 } 58 }
58 59
59 // translate the fields 60 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 61 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 62 for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 63 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 64 translator.getTranslatedClass(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 65 translator.getTranslatedFieldDef(mapEntry.getValue())
65 ); 66 );
66 } 67 }
67 68
68 this.behaviorEntries = HashMultimap.create(); 69 this.methodEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 70 for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) {
70 this.behaviorEntries.put( 71 this.methodEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 72 translator.getTranslatedClass(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 73 translator.getTranslatedMethodDef(mapEntry.getValue())
73 ); 74 );
74 } 75 }
75 } 76 }
76 77
77 public void indexClass(CtClass c) { 78 protected ClassDefEntry indexClass(int access, String name, String superName, String[] interfaces) {
78 indexClass(c, true); 79 ClassDefEntry classEntry = new ClassDefEntry(name, new AccessFlags(access));
79 }
80
81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 80 if (isJre(classEntry)) {
84 return; 81 return null;
85 } 82 }
86 83
87 // add the superclass 84 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 85 ClassEntry superclassEntry = entryPool.getClass(superName);
89 if (superclassEntry != null) { 86 if (!isJre(superclassEntry)) {
90 this.superclasses.put(classEntry, superclassEntry); 87 this.superclasses.put(classEntry, superclassEntry);
91 } 88 }
92 89
93 // add the interfaces 90 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 91 for (String interfaceClassName : interfaces) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 92 ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName);
96 if (!isJre(interfaceClassEntry)) { 93 if (!isJre(interfaceClassEntry)) {
97
98 this.interfaces.put(classEntry, interfaceClassEntry); 94 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 95 }
100 } 96 }
101 97
102 if (indexMembers) { 98 return classEntry;
103 // add fields 99 }
104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 }
108 100
109 // add behaviors 101 protected void indexField(FieldDefEntry fieldEntry) {
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 102 this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 103 }
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 104
113 } 105 protected void indexMethod(MethodDefEntry methodEntry) {
114 } 106 this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry);
115 } 107 }
116 108
117 public void renameClasses(Map<String, String> renames) { 109 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 110 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 111 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 112 EntryRenamer.renameClassesInMultimap(renames, this.methodEntries);
121 } 113 }
122 114
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 115 public ClassEntry getSuperclass(ClassEntry classEntry) {
@@ -175,31 +167,32 @@ public class TranslationIndex {
175 } 167 }
176 168
177 public boolean entryExists(Entry entry) { 169 public boolean entryExists(Entry entry) {
170 if (entry == null) {
171 return false;
172 }
178 if (entry instanceof FieldEntry) { 173 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 174 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 175 } else if (entry instanceof MethodEntry) {
181 return behaviorExists((BehaviorEntry) entry); 176 return methodExists((MethodEntry) entry);
182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 177 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 178 return methodExists(((LocalVariableEntry) entry).getOwnerEntry());
186 } 179 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 180 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 181 }
189 182
190 public boolean fieldExists(FieldEntry fieldEntry) { 183 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 184 return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry);
192 } 185 }
193 186
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 187 public boolean methodExists(MethodEntry methodEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 188 return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry);
196 } 189 }
197 190
198 public ClassEntry resolveEntryClass(Entry entry) { 191 public ClassEntry resolveEntryOwner(Entry entry) {
199 return resolveEntryClass(entry, false); 192 return resolveEntryOwner(entry, false);
200 } 193 }
201 194
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 195 public ClassEntry resolveEntryOwner(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 196 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 197 return (ClassEntry) entry;
205 } 198 }
@@ -227,12 +220,12 @@ public class TranslationIndex {
227 Entry originalEntry = entry; 220 Entry originalEntry = entry;
228 221
229 // Get all possible superclasses and reverse the list 222 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 223 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getOwnerClassEntry()));
231 224
232 boolean existInEntry = false; 225 boolean existInEntry = false;
233 226
234 for (ClassEntry classEntry : superclasses) { 227 for (ClassEntry classEntry : superclasses) {
235 entry = entry.cloneToNewClass(classEntry); 228 entry = entry.updateOwnership(classEntry);
236 existInEntry = entryExists(entry); 229 existInEntry = entryExists(entry);
237 230
238 // Check for possible entry in interfaces of superclasses 231 // Check for possible entry in interfaces of superclasses
@@ -245,9 +238,9 @@ public class TranslationIndex {
245 238
246 // Doesn't exists in superclasses? check the child or return null 239 // Doesn't exists in superclasses? check the child or return null
247 if (!existInEntry) 240 if (!existInEntry)
248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 241 return !entryExists(originalEntry) ? null : originalEntry.getOwnerClassEntry();
249 242
250 return entry.getClassEntry(); 243 return entry.getOwnerClassEntry();
251 } 244 }
252 245
253 public ClassEntry resolveSuperclass(Entry entry) { 246 public ClassEntry resolveSuperclass(Entry entry) {
@@ -256,7 +249,7 @@ public class TranslationIndex {
256 249
257 while (!entryExists(entry)) { 250 while (!entryExists(entry)) {
258 // is there a parent class? 251 // is there a parent class?
259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 252 ClassEntry superclassEntry = getSuperclass(entry.getOwnerClassEntry());
260 if (superclassEntry == null) { 253 if (superclassEntry == null) {
261 // this is probably a method from a class in a library 254 // this is probably a method from a class in a library
262 // we can't trace the implementation up any higher unless we index the library 255 // we can't trace the implementation up any higher unless we index the library
@@ -264,23 +257,23 @@ public class TranslationIndex {
264 } 257 }
265 258
266 // move up to the parent class 259 // move up to the parent class
267 entry = entry.cloneToNewClass(superclassEntry); 260 entry = entry.updateOwnership(superclassEntry);
268 } 261 }
269 return entry.getClassEntry(); 262 return entry.getOwnerClassEntry();
270 } 263 }
271 264
272 public ClassEntry resolveInterface(Entry entry) { 265 public ClassEntry resolveInterface(Entry entry) {
273 // the interfaces for any class is a forest 266 // the interfaces for any class is a forest
274 // so let's look at all the trees 267 // so let's look at all the trees
275 268
276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 269 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getOwnerClassEntry())) {
277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 270 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 if (subInterface != null && !subInterface.isEmpty()) { 271 if (subInterface != null && !subInterface.isEmpty()) {
279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 272 ClassEntry result = resolveInterface(entry.updateOwnership(interfaceEntry));
280 if (result != null) 273 if (result != null)
281 return result; 274 return result;
282 } 275 }
283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 276 ClassEntry resolvedClassEntry = resolveSuperclass(entry.updateOwnership(interfaceEntry));
284 if (resolvedClassEntry != null) { 277 if (resolvedClassEntry != null) {
285 return resolvedClassEntry; 278 return resolvedClassEntry;
286 } 279 }
diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
new file mode 100644
index 0000000..0999abf
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
@@ -0,0 +1,79 @@
1package cuchaz.enigma.bytecode;
2
3import org.objectweb.asm.Opcodes;
4
5import java.lang.reflect.Modifier;
6
7public class AccessFlags {
8 private static final int SYNTHETIC_FLAG = 0x00001000;
9 private static final int BRIDGED_FLAG = 0x00000040;
10
11 private int flags;
12
13 public AccessFlags(int flags) {
14 this.flags = flags;
15 }
16
17 public boolean isPrivate() {
18 return Modifier.isPrivate(this.flags);
19 }
20
21 public boolean isProtected() {
22 return Modifier.isProtected(this.flags);
23 }
24
25 public boolean isPublic() {
26 return Modifier.isPublic(this.flags);
27 }
28
29 public boolean isSynthetic() {
30 return (this.flags & SYNTHETIC_FLAG) != 0;
31 }
32
33 public boolean isStatic() {
34 return Modifier.isStatic(this.flags);
35 }
36
37 public AccessFlags setPrivate() {
38 this.setVisibility(Opcodes.ACC_PRIVATE);
39 return this;
40 }
41
42 public AccessFlags setProtected() {
43 this.setVisibility(Opcodes.ACC_PROTECTED);
44 return this;
45 }
46
47 public AccessFlags setPublic() {
48 this.setVisibility(Opcodes.ACC_PUBLIC);
49 return this;
50 }
51
52 public AccessFlags setBridged() {
53 this.setVisibility(BRIDGED_FLAG);
54 return this;
55 }
56
57 public void setVisibility(int visibility) {
58 this.resetVisibility();
59 this.flags |= visibility;
60 }
61
62 private void resetVisibility() {
63 this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
64 }
65
66 public int getFlags() {
67 return this.flags;
68 }
69
70 @Override
71 public boolean equals(Object obj) {
72 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags;
73 }
74
75 @Override
76 public int hashCode() {
77 return flags;
78 }
79}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
index 6ec576e..9ed6db9 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java
@@ -11,41 +11,39 @@
11 11
12package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
13 13
14import javassist.CtBehavior; 14import org.objectweb.asm.ClassVisitor;
15import javassist.CtClass; 15import org.objectweb.asm.FieldVisitor;
16import javassist.CtField; 16import org.objectweb.asm.MethodVisitor;
17import javassist.bytecode.AccessFlag;
18import javassist.bytecode.InnerClassesAttribute;
19 17
20public class ClassProtectifier { 18public class ClassProtectifier extends ClassVisitor {
21 19
22 public static CtClass protectify(CtClass c) { 20 public ClassProtectifier(int api, ClassVisitor cv) {
23 21 super(api, cv);
24 // protectify all the fields 22 }
25 for (CtField field : c.getDeclaredFields()) {
26 field.setModifiers(protectify(field.getModifiers()));
27 }
28 23
29 // protectify all the methods and constructors 24 @Override
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 25 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 behavior.setModifiers(protectify(behavior.getModifiers())); 26 access = protectify(access);
32 } 27 return super.visitMethod(access, name, desc, signature, exceptions);
28 }
33 29
34 // protectify all the inner classes 30 @Override
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 31 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
36 if (attr != null) { 32 access = protectify(access);
37 for (int i = 0; i < attr.tableLength(); i++) { 33 return super.visitField(access, name, desc, signature, value);
38 attr.setAccessFlags(i, protectify(attr.accessFlags(i))); 34 }
39 }
40 }
41 35
42 return c; 36 @Override
37 public void visitInnerClass(String name, String outerName, String innerName, int access) {
38 access = protectify(access);
39 super.visitInnerClass(name, outerName, innerName, access);
43 } 40 }
44 41
45 private static int protectify(int flags) { 42 private static int protectify(int access) {
46 if (AccessFlag.isPrivate(flags)) { 43 AccessFlags accessFlags = new AccessFlags(access);
47 flags = AccessFlag.setProtected(flags); 44 if (accessFlags.isPrivate()) {
45 accessFlags.setProtected();
48 } 46 }
49 return flags; 47 return accessFlags.getFlags();
50 } 48 }
51} 49}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
index d627fe9..64de788 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java
@@ -11,41 +11,45 @@
11 11
12package cuchaz.enigma.bytecode; 12package cuchaz.enigma.bytecode;
13 13
14import javassist.CtBehavior; 14import org.objectweb.asm.ClassVisitor;
15import javassist.CtClass; 15import org.objectweb.asm.FieldVisitor;
16import javassist.CtField; 16import org.objectweb.asm.MethodVisitor;
17import javassist.bytecode.AccessFlag;
18import javassist.bytecode.InnerClassesAttribute;
19 17
20public class ClassPublifier { 18public class ClassPublifier extends ClassVisitor {
21 19
22 public static CtClass publify(CtClass c) { 20 public ClassPublifier(int api, ClassVisitor cv) {
21 super(api, cv);
22 }
23 23
24 // publify all the fields 24 @Override
25 for (CtField field : c.getDeclaredFields()) { 25 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
26 field.setModifiers(publify(field.getModifiers())); 26 access = publify(access);
27 } 27 super.visit(version, access, name, signature, superName, interfaces);
28 }
28 29
29 // publify all the methods and constructors 30 @Override
30 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 31 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
31 behavior.setModifiers(publify(behavior.getModifiers())); 32 access = publify(access);
32 } 33 return super.visitField(access, name, desc, signature, value);
34 }
33 35
34 // publify all the inner classes 36 @Override
35 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); 37 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
36 if (attr != null) { 38 access = publify(access);
37 for (int i = 0; i < attr.tableLength(); i++) { 39 return super.visitMethod(access, name, desc, signature, exceptions);
38 attr.setAccessFlags(i, publify(attr.accessFlags(i))); 40 }
39 }
40 }
41 41
42 return c; 42 @Override
43 public void visitInnerClass(String name, String outerName, String innerName, int access) {
44 access = publify(access);
45 super.visitInnerClass(name, outerName, innerName, access);
43 } 46 }
44 47
45 private static int publify(int flags) { 48 private static int publify(int access) {
46 if (!AccessFlag.isPublic(flags)) { 49 AccessFlags accessFlags = new AccessFlags(access);
47 flags = AccessFlag.setPublic(flags); 50 if (!accessFlags.isPublic()) {
51 accessFlags.setPublic();
48 } 52 }
49 return flags; 53 return accessFlags.getFlags();
50 } 54 }
51} 55}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
deleted file mode 100644
index 62a838d..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
+++ /dev/null
@@ -1,539 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode;
13
14import cuchaz.enigma.mapping.ClassEntry;
15import cuchaz.enigma.mapping.ClassNameReplacer;
16import cuchaz.enigma.mapping.Mappings;
17import cuchaz.enigma.mapping.Translator;
18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
21import javassist.Modifier;
22import javassist.bytecode.*;
23import javassist.bytecode.SignatureAttribute.*;
24
25import java.lang.reflect.InvocationTargetException;
26import java.lang.reflect.Method;
27import java.util.Arrays;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
32public class ClassRenamer {
33
34 public static void applyModifier(Object obj, Mappings.EntryModifier modifier) {
35 int mod = -1;
36 if (obj instanceof CtField)
37 mod = ((CtField) obj).getModifiers();
38 else if (obj instanceof CtBehavior)
39 mod = ((CtBehavior) obj).getModifiers();
40 else if (obj instanceof CtClass)
41 mod = ((CtClass) obj).getModifiers();
42
43 if (mod != -1) {
44 switch (modifier) {
45 case PRIVATE:
46 mod = Modifier.setPrivate(mod);
47 break;
48 case PROTECTED:
49 mod = Modifier.setProtected(mod);
50 break;
51 case PUBLIC:
52 mod = Modifier.setPublic(mod);
53 break;
54 default:
55 break;
56 }
57 if (obj instanceof CtField)
58 ((CtField) obj).setModifiers(mod);
59 else if (obj instanceof CtBehavior)
60 ((CtBehavior) obj).setModifiers(mod);
61 else
62 ((CtClass) obj).setModifiers(mod);
63 }
64 }
65
66 public static void renameClasses(CtClass c, final Translator translator) {
67 renameClasses(c, className -> {
68 ClassEntry entry = translator.translateEntry(new ClassEntry(className));
69 if (entry != null) {
70 return entry.getName();
71 }
72 return null;
73 });
74 }
75
76 public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) {
77 renameClasses(c, className -> {
78 ClassEntry entry = new ClassEntry(className);
79 if (entry.isInDefaultPackage()) {
80 return newPackageName + "/" + entry.getName();
81 }
82 return null;
83 });
84 }
85
86 public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) {
87 renameClasses(c, className -> {
88 ClassEntry entry = new ClassEntry(className);
89 if (entry.getPackageName().equals(oldPackageName)) {
90 return entry.getSimpleName();
91 }
92 return null;
93 });
94 }
95
96 @SuppressWarnings("unchecked")
97 public static void renameClasses(CtClass c, ClassNameReplacer replacer) {
98
99 // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =(
100
101 ReplacerClassMap map = new ReplacerClassMap(replacer);
102 ClassFile classFile = c.getClassFile();
103
104 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
105 ConstPool constPool = c.getClassFile().getConstPool();
106 constPool.renameClass(map);
107
108 // rename class attributes
109 renameAttributes(classFile.getAttributes(), map, SignatureType.Class);
110
111 // rename methods
112 for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) {
113 methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map));
114 renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method);
115 }
116
117 // rename fields
118 for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) {
119 fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map));
120 renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field);
121 }
122
123 // rename the class name itself last
124 // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass()
125 // we only want to replace exactly this class name
126 String newName = renameClassName(c.getName(), map);
127 if (newName != null) {
128 c.setName(newName);
129 }
130
131 // replace simple names in the InnerClasses attribute too
132 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
133 if (attr != null) {
134 for (int i = 0; i < attr.tableLength(); i++) {
135
136 String innerName = attr.innerClass(i);
137 // get the inner class full name (which has already been translated)
138 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
139
140 if (attr.innerNameIndex(i) != 0) {
141 // update the inner name
142 attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName()));
143 }
144
145 /* DEBUG
146 System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i)));
147 */
148 }
149 }
150 }
151
152 @SuppressWarnings("unchecked")
153 private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) {
154 try {
155
156 // make the rename class method accessible
157 Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class);
158 renameClassMethod.setAccessible(true);
159
160 for (AttributeInfo attribute : attributes) {
161 if (attribute instanceof SignatureAttribute) {
162 // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell
163 SignatureAttribute signatureAttribute = (SignatureAttribute) attribute;
164 String newSignature = type.rename(signatureAttribute.getSignature(), map);
165 if (newSignature != null) {
166 signatureAttribute.setSignature(newSignature);
167 }
168 } else if (attribute instanceof CodeAttribute) {
169 // code attributes have signature attributes too (indirectly)
170 CodeAttribute codeAttribute = (CodeAttribute) attribute;
171 renameAttributes(codeAttribute.getAttributes(), map, type);
172 } else if (attribute instanceof LocalVariableTypeAttribute) {
173 // lvt attributes have signature attributes too
174 LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute;
175 renameLocalVariableTypeAttribute(localVariableAttribute, map);
176 } else {
177 renameClassMethod.invoke(attribute, map);
178 }
179 }
180
181 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
182 throw new Error("Unable to call javassist methods by reflection!", ex);
183 }
184 }
185
186 private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) {
187
188 // adapted from LocalVariableAttribute.renameClass()
189 ConstPool cp = attribute.getConstPool();
190 int n = attribute.tableLength();
191 byte[] info = attribute.get();
192 for (int i = 0; i < n; ++i) {
193 int pos = i * 10 + 2;
194 int index = ByteArray.readU16bit(info, pos + 6);
195 if (index != 0) {
196 String signature = cp.getUtf8Info(index);
197 String newSignature = renameLocalVariableSignature(signature, map);
198 if (newSignature != null) {
199 ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6);
200 }
201 }
202 }
203 }
204
205 private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) {
206
207 // for some reason, signatures with . in them don't count as field signatures
208 // looks like anonymous classes delimit with . in stead of $
209 // convert the . to $, but keep track of how many we replace
210 // we need to put them back after we translate
211 int start = signature.lastIndexOf('$') + 1;
212 int numConverted = 0;
213 StringBuilder buf = new StringBuilder(signature);
214 for (int i = buf.length() - 1; i >= start; i--) {
215 char c = buf.charAt(i);
216 if (c == '.') {
217 buf.setCharAt(i, '$');
218 numConverted++;
219 }
220 }
221 signature = buf.toString();
222
223 // translate
224 String newSignature = renameFieldSignature(signature, map);
225 if (newSignature != null) {
226
227 // put the delimiters back
228 buf = new StringBuilder(newSignature);
229 for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) {
230 char c = buf.charAt(i);
231 if (c == '$') {
232 buf.setCharAt(i, '.');
233 numConverted--;
234 }
235 }
236 assert (numConverted == 0);
237 newSignature = buf.toString();
238
239 return newSignature;
240 }
241
242 return null;
243 }
244
245 private static String renameClassSignature(String signature, ReplacerClassMap map) {
246 try {
247 ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map);
248 return type.encode();
249 } catch (BadBytecode ex) {
250 throw new Error("Can't parse field signature: " + signature);
251 }
252 }
253
254 private static String renameFieldSignature(String signature, ReplacerClassMap map) {
255 try {
256 ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map);
257 if (type != null) {
258 return type.encode();
259 }
260 return null;
261 } catch (BadBytecode ex) {
262 throw new Error("Can't parse class signature: " + signature);
263 }
264 }
265
266 private static String renameMethodSignature(String signature, ReplacerClassMap map) {
267 try {
268 MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map);
269 return type.encode();
270 } catch (BadBytecode ex) {
271 throw new Error("Can't parse method signature: " + signature);
272 }
273 }
274
275 private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) {
276 if (typeParamTypes != null) {
277 typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length);
278 for (int i = 0; i < typeParamTypes.length; i++) {
279 TypeParameter newParamType = renameType(typeParamTypes[i], map);
280 if (newParamType != null) {
281 typeParamTypes[i] = newParamType;
282 }
283 }
284 }
285 return typeParamTypes;
286 }
287
288 private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) {
289
290 TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map);
291
292 ClassType superclassType = type.getSuperClass();
293 if (superclassType != ClassType.OBJECT) {
294 ClassType newSuperclassType = renameType(superclassType, map);
295 if (newSuperclassType != null) {
296 superclassType = newSuperclassType;
297 }
298 }
299
300 ClassType[] interfaceTypes = type.getInterfaces();
301 if (interfaceTypes != null) {
302 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
303 for (int i = 0; i < interfaceTypes.length; i++) {
304 ClassType newInterfaceType = renameType(interfaceTypes[i], map);
305 if (newInterfaceType != null) {
306 interfaceTypes[i] = newInterfaceType;
307 }
308 }
309 }
310
311 return new ClassSignature(typeParamTypes, superclassType, interfaceTypes);
312 }
313
314 private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) {
315
316 TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map);
317
318 Type[] paramTypes = type.getParameterTypes();
319 if (paramTypes != null) {
320 paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
321 for (int i = 0; i < paramTypes.length; i++) {
322 Type newParamType = renameType(paramTypes[i], map);
323 if (newParamType != null) {
324 paramTypes[i] = newParamType;
325 }
326 }
327 }
328
329 Type returnType = type.getReturnType();
330 if (returnType != null) {
331 Type newReturnType = renameType(returnType, map);
332 if (newReturnType != null) {
333 returnType = newReturnType;
334 }
335 }
336
337 ObjectType[] exceptionTypes = type.getExceptionTypes();
338 if (exceptionTypes != null) {
339 exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length);
340 for (int i = 0; i < exceptionTypes.length; i++) {
341 ObjectType newExceptionType = renameType(exceptionTypes[i], map);
342 if (newExceptionType != null) {
343 exceptionTypes[i] = newExceptionType;
344 }
345 }
346 }
347
348 return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes);
349 }
350
351 private static Type renameType(Type type, ReplacerClassMap map) {
352 if (type instanceof ObjectType) {
353 return renameType((ObjectType) type, map);
354 } else if (type instanceof BaseType) {
355 return renameType((BaseType) type, map);
356 } else {
357 throw new Error("Don't know how to rename type " + type.getClass());
358 }
359 }
360
361 private static ObjectType renameType(ObjectType type, ReplacerClassMap map) {
362 if (type instanceof ArrayType) {
363 return renameType((ArrayType) type, map);
364 } else if (type instanceof ClassType) {
365 return renameType((ClassType) type, map);
366 } else if (type instanceof TypeVariable) {
367 return renameType((TypeVariable) type, map);
368 } else {
369 throw new Error("Don't know how to rename type " + type.getClass());
370 }
371 }
372
373 private static BaseType renameType(BaseType type, ReplacerClassMap map) {
374 // don't have to rename primitives
375 return null;
376 }
377
378 private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) {
379 // don't have to rename template args
380 return null;
381 }
382
383 private static ClassType renameType(ClassType type, ReplacerClassMap map) {
384
385 // translate type args
386 TypeArgument[] args = type.getTypeArguments();
387 if (args != null) {
388 args = Arrays.copyOf(args, args.length);
389 for (int i = 0; i < args.length; i++) {
390 TypeArgument newType = renameType(args[i], map);
391 if (newType != null) {
392 args[i] = newType;
393 }
394 }
395 }
396
397 if (type instanceof NestedClassType) {
398 NestedClassType nestedType = (NestedClassType) type;
399
400 // translate the name
401 String name = getClassName(type);
402 String newName = map.get(name);
403 if (newName != null) {
404 name = new ClassEntry(newName).getInnermostClassName();
405 }
406
407 // translate the parent class too
408 ClassType parent = renameType(nestedType.getDeclaringClass(), map);
409 if (parent == null) {
410 parent = nestedType.getDeclaringClass();
411 }
412
413 return new NestedClassType(parent, name, args);
414 } else {
415
416 // translate the name
417 String name = type.getName();
418 String newName = renameClassName(name, map);
419 if (newName != null) {
420 name = newName;
421 }
422
423 return new ClassType(name, args);
424 }
425 }
426
427 private static String getClassName(ClassType type) {
428 if (type instanceof NestedClassType) {
429 NestedClassType nestedType = (NestedClassType) type;
430 return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$'));
431 } else {
432 return Descriptor.toJvmName(type.getName());
433 }
434 }
435
436 private static String renameClassName(String name, ReplacerClassMap map) {
437 String newName = map.get(Descriptor.toJvmName(name));
438 if (newName != null) {
439 return Descriptor.toJavaName(newName);
440 }
441 return null;
442 }
443
444 private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) {
445 ObjectType subType = type.getType();
446 if (subType != null) {
447 ObjectType newSubType = renameType(subType, map);
448 if (newSubType != null) {
449 switch (type.getKind()) {
450 case ' ':
451 return new TypeArgument(newSubType);
452 case '+':
453 return TypeArgument.subclassOf(newSubType);
454 case '-':
455 return TypeArgument.superOf(newSubType);
456 default:
457 throw new Error("Unknown type kind: " + type.getKind());
458 }
459 }
460 }
461 return null;
462 }
463
464 private static ArrayType renameType(ArrayType type, ReplacerClassMap map) {
465 Type newSubType = renameType(type.getComponentType(), map);
466 if (newSubType != null) {
467 return new ArrayType(type.getDimension(), newSubType);
468 }
469 return null;
470 }
471
472 private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) {
473
474 ObjectType superclassType = type.getClassBound();
475 if (superclassType != null) {
476 ObjectType newSuperclassType = renameType(superclassType, map);
477 if (newSuperclassType != null) {
478 superclassType = newSuperclassType;
479 }
480 }
481
482 ObjectType[] interfaceTypes = type.getInterfaceBound();
483 if (interfaceTypes != null) {
484 interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length);
485 for (int i = 0; i < interfaceTypes.length; i++) {
486 ObjectType newInterfaceType = renameType(interfaceTypes[i], map);
487 if (newInterfaceType != null) {
488 interfaceTypes[i] = newInterfaceType;
489 }
490 }
491 }
492
493 return new TypeParameter(type.getName(), superclassType, interfaceTypes);
494 }
495
496 private enum SignatureType {
497 Class {
498 @Override
499 public String rename(String signature, ReplacerClassMap map) {
500 return renameClassSignature(signature, map);
501 }
502 },
503 Field {
504 @Override
505 public String rename(String signature, ReplacerClassMap map) {
506 return renameFieldSignature(signature, map);
507 }
508 },
509 Method {
510 @Override
511 public String rename(String signature, ReplacerClassMap map) {
512 return renameMethodSignature(signature, map);
513 }
514 };
515
516 public abstract String rename(String signature, ReplacerClassMap map);
517 }
518
519 private static class ReplacerClassMap extends HashMap<String, String> {
520
521 private ClassNameReplacer replacer;
522
523 public ReplacerClassMap(ClassNameReplacer replacer) {
524 this.replacer = replacer;
525 }
526
527 @Override
528 public String get(Object obj) {
529 if (obj instanceof String) {
530 return get((String) obj);
531 }
532 return null;
533 }
534
535 public String get(String className) {
536 return replacer.replace(className);
537 }
538 }
539}
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
deleted file mode 100644
index 1932730..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java
+++ /dev/null
@@ -1,264 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode;
13
14import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
15import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
16import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
17import javassist.bytecode.ConstPool;
18import javassist.bytecode.Descriptor;
19
20import java.io.DataInputStream;
21import java.io.DataOutputStream;
22import java.lang.reflect.Constructor;
23import java.lang.reflect.Field;
24import java.lang.reflect.Method;
25import java.util.HashMap;
26
27public class ConstPoolEditor {
28
29 private static Method getItem;
30 private static Method addItem;
31 private static Method addItem0;
32 private static Field items;
33 private static Field cache;
34 private static Field numItems;
35 private static Field objects;
36 private static Field elements;
37 private static Method methodWritePool;
38 private static Constructor<ConstPool> constructorPool;
39
40 static {
41 try {
42 getItem = ConstPool.class.getDeclaredMethod("getItem", int.class);
43 getItem.setAccessible(true);
44
45 addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo"));
46 addItem.setAccessible(true);
47
48 addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo"));
49 addItem0.setAccessible(true);
50
51 items = ConstPool.class.getDeclaredField("items");
52 items.setAccessible(true);
53
54 cache = ConstPool.class.getDeclaredField("itemsCache");
55 cache.setAccessible(true);
56
57 numItems = ConstPool.class.getDeclaredField("numOfItems");
58 numItems.setAccessible(true);
59
60 objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects");
61 objects.setAccessible(true);
62
63 elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements");
64 elements.setAccessible(true);
65
66 methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class);
67 methodWritePool.setAccessible(true);
68
69 constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class);
70 constructorPool.setAccessible(true);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75
76 private ConstPool pool;
77
78 public ConstPoolEditor(ConstPool pool) {
79 this.pool = pool;
80 }
81
82 public static ConstPool readPool(DataInputStream in) {
83 try {
84 return constructorPool.newInstance(in);
85 } catch (Exception ex) {
86 throw new Error(ex);
87 }
88 }
89
90 public static ConstPool newConstPool() {
91 // const pool expects the name of a class to initialize itself
92 // but we want an empty pool
93 // so give it a bogus name, and then clear the entries afterwards
94 ConstPool pool = new ConstPool("a");
95
96 ConstPoolEditor editor = new ConstPoolEditor(pool);
97 int size = pool.getSize();
98 for (int i = 0; i < size - 1; i++) {
99 editor.removeLastItem();
100 }
101
102 // make sure the pool is actually empty
103 // although, in this case "empty" means one thing in it
104 // the JVM spec says index 0 should be reserved
105 assert (pool.getSize() == 1);
106 assert (editor.getItem(0) == null);
107 assert (editor.getItem(1) == null);
108 assert (editor.getItem(2) == null);
109 assert (editor.getItem(3) == null);
110
111 // also, clear the cache
112 editor.getCache().clear();
113
114 return pool;
115 }
116
117 public void writePool(DataOutputStream out) {
118 try {
119 methodWritePool.invoke(this.pool, out);
120 } catch (Exception ex) {
121 throw new Error(ex);
122 }
123 }
124
125 public String getMemberrefClassname(int memberrefIndex) {
126 return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex)));
127 }
128
129 public String getMemberrefName(int memberrefIndex) {
130 return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex)));
131 }
132
133 public String getMemberrefType(int memberrefIndex) {
134 return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex)));
135 }
136
137 public ConstInfoAccessor getItem(int index) {
138 try {
139 Object entry = getItem.invoke(this.pool, index);
140 if (entry == null) {
141 return null;
142 }
143 return new ConstInfoAccessor(entry);
144 } catch (Exception ex) {
145 throw new Error(ex);
146 }
147 }
148
149 public int addItem(Object item) {
150 try {
151 return (Integer) addItem.invoke(this.pool, item);
152 } catch (Exception ex) {
153 throw new Error(ex);
154 }
155 }
156
157 public int addItemForceNew(Object item) {
158 try {
159 return (Integer) addItem0.invoke(this.pool, item);
160 } catch (Exception ex) {
161 throw new Error(ex);
162 }
163 }
164
165 @SuppressWarnings("rawtypes")
166 public void removeLastItem() {
167 try {
168 // remove the item from the cache
169 HashMap cache = getCache();
170 if (cache != null) {
171 Object item = getItem(this.pool.getSize() - 1);
172 cache.remove(item);
173 }
174
175 // remove the actual item
176 // based off of LongVector.addElement()
177 Object item = items.get(this.pool);
178 Object[][] object = (Object[][]) objects.get(items);
179 int numElements = (Integer) elements.get(items) - 1;
180 int nth = numElements >> 7;
181 int offset = numElements & (128 - 1);
182 object[nth][offset] = null;
183
184 // decrement the number of items
185 elements.set(item, numElements);
186 numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1);
187 } catch (Exception ex) {
188 throw new Error(ex);
189 }
190 }
191
192 @SuppressWarnings("rawtypes")
193 public HashMap getCache() {
194 try {
195 return (HashMap) cache.get(this.pool);
196 } catch (Exception ex) {
197 throw new Error(ex);
198 }
199 }
200
201 @SuppressWarnings({ "rawtypes", "unchecked" })
202 public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) {
203 // NOTE: when changing values, we always need to copy-on-write
204 try {
205 // get the memberref item
206 Object item = getItem(memberrefIndex).getItem();
207
208 // update the cache
209 HashMap cache = getCache();
210 if (cache != null) {
211 cache.remove(item);
212 }
213
214 new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType));
215
216 // update the cache
217 if (cache != null) {
218 cache.put(item, item);
219 }
220 } catch (Exception ex) {
221 throw new Error(ex);
222 }
223
224 // make sure the change worked
225 assert (newName.equals(getMemberrefName(memberrefIndex)));
226 assert (newType.equals(getMemberrefType(memberrefIndex)));
227 }
228
229 @SuppressWarnings({ "rawtypes", "unchecked" })
230 public void changeClassName(int classNameIndex, String newName) {
231 // NOTE: when changing values, we always need to copy-on-write
232 try {
233 // get the class item
234 Object item = getItem(classNameIndex).getItem();
235
236 // update the cache
237 HashMap cache = getCache();
238 if (cache != null) {
239 cache.remove(item);
240 }
241
242 // add the new name and repoint the name-and-type to it
243 new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName));
244
245 // update the cache
246 if (cache != null) {
247 cache.put(item, item);
248 }
249 } catch (Exception ex) {
250 throw new Error(ex);
251 }
252 }
253
254 public String dump() {
255 StringBuilder buf = new StringBuilder();
256 for (int i = 1; i < this.pool.getSize(); i++) {
257 buf.append(String.format("%4d", i));
258 buf.append(" ");
259 buf.append(getItem(i));
260 buf.append("\n");
261 }
262 return buf.toString();
263 }
264}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java
deleted file mode 100644
index 9013d58..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java
+++ /dev/null
@@ -1,266 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode;
13
14import com.google.common.collect.Maps;
15import cuchaz.enigma.bytecode.accessors.*;
16
17import java.util.Collection;
18import java.util.Map;
19
20public enum InfoType {
21
22 Utf8Info(1),
23 IntegerInfo(3),
24 FloatInfo(4),
25 LongInfo(5),
26 DoubleInfo(6),
27 ClassInfo(7) {
28 @Override
29 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
30 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
31 gatherIndexTree(indices, editor, accessor.getNameIndex());
32 }
33
34 @Override
35 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
36 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
37 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
38 }
39
40 @Override
41 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
42 ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem());
43 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
44 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
45 }
46 },
47 StringInfo(8) {
48 @Override
49 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
50 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
51 gatherIndexTree(indices, editor, accessor.getStringIndex());
52 }
53
54 @Override
55 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
56 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
57 accessor.setStringIndex(remapIndex(map, accessor.getStringIndex()));
58 }
59
60 @Override
61 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
62 StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem());
63 ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex());
64 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
65 }
66 },
67 FieldRefInfo(9) {
68 @Override
69 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
70 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
71 gatherIndexTree(indices, editor, accessor.getClassIndex());
72 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
73 }
74
75 @Override
76 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
77 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
78 accessor.setClassIndex(remapIndex(map, accessor.getClassIndex()));
79 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
80 }
81
82 @Override
83 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
84 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem());
85 ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex());
86 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
87 return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
88 }
89 },
90 // same as FieldRefInfo
91 MethodRefInfo(10) {
92 @Override
93 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
94 FieldRefInfo.gatherIndexTree(indices, editor, entry);
95 }
96
97 @Override
98 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
99 FieldRefInfo.remapIndices(map, entry);
100 }
101
102 @Override
103 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
104 return FieldRefInfo.subIndicesAreValid(entry, pool);
105 }
106 },
107 // same as FieldRefInfo
108 InterfaceMethodRefInfo(11) {
109 @Override
110 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
111 FieldRefInfo.gatherIndexTree(indices, editor, entry);
112 }
113
114 @Override
115 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
116 FieldRefInfo.remapIndices(map, entry);
117 }
118
119 @Override
120 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
121 return FieldRefInfo.subIndicesAreValid(entry, pool);
122 }
123 },
124 NameAndTypeInfo(12) {
125 @Override
126 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
127 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
128 gatherIndexTree(indices, editor, accessor.getNameIndex());
129 gatherIndexTree(indices, editor, accessor.getTypeIndex());
130 }
131
132 @Override
133 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
134 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
135 accessor.setNameIndex(remapIndex(map, accessor.getNameIndex()));
136 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
137 }
138
139 @Override
140 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
141 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem());
142 ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex());
143 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
144 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
145 }
146 },
147 MethodHandleInfo(15) {
148 @Override
149 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
150 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
151 gatherIndexTree(indices, editor, accessor.getTypeIndex());
152 gatherIndexTree(indices, editor, accessor.getMethodRefIndex());
153 }
154
155 @Override
156 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
157 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
158 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
159 accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex()));
160 }
161
162 @Override
163 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
164 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem());
165 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
166 ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex());
167 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
168 }
169 },
170 MethodTypeInfo(16) {
171 @Override
172 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
173 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
174 gatherIndexTree(indices, editor, accessor.getTypeIndex());
175 }
176
177 @Override
178 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
179 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
180 accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex()));
181 }
182
183 @Override
184 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
185 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem());
186 ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex());
187 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
188 }
189 },
190 InvokeDynamicInfo(18) {
191 @Override
192 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
193 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
194 gatherIndexTree(indices, editor, accessor.getBootstrapIndex());
195 gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex());
196 }
197
198 @Override
199 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
200 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
201 accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex()));
202 accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex()));
203 }
204
205 @Override
206 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
207 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem());
208 ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex());
209 ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex());
210 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
211 }
212 };
213
214 private static Map<Integer, InfoType> types;
215
216 static {
217 types = Maps.newTreeMap();
218 for (InfoType type : values()) {
219 types.put(type.getTag(), type);
220 }
221 }
222
223 private int tag;
224
225 InfoType(int tag) {
226 this.tag = tag;
227 }
228
229 public static InfoType getByTag(int tag) {
230 return types.get(tag);
231 }
232
233 public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) {
234 // add own index
235 indices.add(index);
236
237 // recurse
238 ConstInfoAccessor entry = editor.getItem(index);
239 entry.getType().gatherIndexTree(indices, editor, entry);
240 }
241
242 private static int remapIndex(Map<Integer, Integer> map, int index) {
243 Integer newIndex = map.get(index);
244 if (newIndex == null) {
245 newIndex = index;
246 }
247 return newIndex;
248 }
249
250 public int getTag() {
251 return this.tag;
252 }
253
254 public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) {
255 // by default, do nothing
256 }
257
258 public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) {
259 // by default, do nothing
260 }
261
262 public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) {
263 // by default, everything is good
264 return true;
265 }
266}
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
deleted file mode 100644
index 3f819ab..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
+++ /dev/null
@@ -1,87 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode;
13
14import javassist.bytecode.AttributeInfo;
15import javassist.bytecode.ConstPool;
16import javassist.bytecode.MethodInfo;
17
18import java.io.ByteArrayOutputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.List;
23
24public class MethodParametersAttribute extends AttributeInfo {
25
26 private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) {
27 super(pool, "MethodParameters", writeStruct(parameterNameIndices));
28 }
29
30 public static void updateClass(MethodInfo info, List<String> names) {
31
32 // add the names to the class const pool
33 ConstPool constPool = info.getConstPool();
34 List<Integer> parameterNameIndices = new ArrayList<>();
35 for (String name : names) {
36 if (name != null) {
37 parameterNameIndices.add(constPool.addUtf8Info(name));
38 } else {
39 parameterNameIndices.add(0);
40 }
41 }
42
43 // add the attribute to the method
44 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices));
45 }
46
47 private static byte[] writeStruct(List<Integer> parameterNameIndices) {
48 // JVM 8 Spec says the struct looks like this:
49 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24
50 // uint8 num_params
51 // for each param:
52 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
53 // uint16 access_flags -> don't care, just set to 0
54
55 ByteArrayOutputStream buf = new ByteArrayOutputStream();
56 DataOutputStream out = new DataOutputStream(buf);
57
58 // NOTE: java hates unsigned integers, so we have to be careful here
59 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
60 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
61 // if the int is out of range, the byte stream won't look the way we want and weird things will happen
62 final int SIZEOF_UINT8 = 1;
63 final int SIZEOF_UINT16 = 2;
64 final int MAX_UINT8 = (1 << 8) - 1;
65 final int MAX_UINT16 = (1 << 16) - 1;
66
67 try {
68 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8);
69 out.writeByte(parameterNameIndices.size());
70
71 for (Integer index : parameterNameIndices) {
72 assert (index >= 0 && index <= MAX_UINT16);
73 out.writeShort(index);
74
75 // just write 0 for the access flags
76 out.writeShort(0);
77 }
78
79 out.close();
80 byte[] data = buf.toByteArray();
81 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16));
82 return data;
83 } catch (IOException ex) {
84 throw new Error(ex);
85 }
86 }
87}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
deleted file mode 100644
index eaa6e90..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
+++ /dev/null
@@ -1,56 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class ClassInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field nameIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.ClassInfo");
24 nameIndex = clazz.getDeclaredField("name");
25 nameIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public ClassInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getNameIndex() {
42 try {
43 return (Integer) nameIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setNameIndex(int val) {
50 try {
51 nameIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
deleted file mode 100644
index 27d991a..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
+++ /dev/null
@@ -1,124 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import com.google.common.base.Charsets;
15import cuchaz.enigma.bytecode.InfoType;
16
17import java.io.*;
18import java.lang.reflect.Field;
19import java.lang.reflect.Method;
20
21public class ConstInfoAccessor {
22
23 private static Class<?> clazz;
24 private static Field index;
25 private static Method getTag;
26
27 static {
28 try {
29 clazz = Class.forName("javassist.bytecode.ConstInfo");
30 index = clazz.getDeclaredField("index");
31 index.setAccessible(true);
32 getTag = clazz.getMethod("getTag");
33 getTag.setAccessible(true);
34 } catch (Exception ex) {
35 throw new Error(ex);
36 }
37 }
38
39 private Object item;
40
41 public ConstInfoAccessor(Object item) {
42 if (item == null) {
43 throw new IllegalArgumentException("item cannot be null!");
44 }
45 this.item = item;
46 }
47
48 public Object getItem() {
49 return this.item;
50 }
51
52 public int getIndex() {
53 try {
54 return (Integer) index.get(this.item);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getTag() {
61 try {
62 return (Integer) getTag.invoke(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public ConstInfoAccessor copy() {
69 return new ConstInfoAccessor(copyItem());
70 }
71
72 public Object copyItem() {
73 // I don't know of a simpler way to copy one of these silly things...
74 try {
75 // serialize the item
76 ByteArrayOutputStream buf = new ByteArrayOutputStream();
77 DataOutputStream out = new DataOutputStream(buf);
78 write(out);
79
80 // deserialize the item
81 DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray()));
82 Object item = new ConstInfoAccessor(in).getItem();
83 in.close();
84
85 return item;
86 } catch (Exception ex) {
87 throw new Error(ex);
88 }
89 }
90
91 public void write(DataOutputStream out) throws IOException {
92 try {
93 out.writeUTF(this.item.getClass().getName());
94 out.writeInt(getIndex());
95
96 Method method = this.item.getClass().getMethod("write", DataOutputStream.class);
97 method.setAccessible(true);
98 method.invoke(this.item, out);
99 } catch (IOException ex) {
100 throw ex;
101 } catch (Exception ex) {
102 throw new Error(ex);
103 }
104 }
105
106 @Override
107 public String toString() {
108 try {
109 ByteArrayOutputStream buf = new ByteArrayOutputStream();
110 PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8));
111 Method print = this.item.getClass().getMethod("print", PrintWriter.class);
112 print.setAccessible(true);
113 print.invoke(this.item, out);
114 out.close();
115 return buf.toString("UTF-8").replace("\n", "");
116 } catch (Exception ex) {
117 throw new Error(ex);
118 }
119 }
120
121 public InfoType getType() {
122 return InfoType.getByTag(getTag());
123 }
124}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
deleted file mode 100644
index aef3532..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
+++ /dev/null
@@ -1,75 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class InvokeDynamicInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field bootstrapIndex;
20 private static Field nameAndTypeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo");
25 bootstrapIndex = clazz.getDeclaredField("bootstrap");
26 bootstrapIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndType");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public InvokeDynamicInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getBootstrapIndex() {
45 try {
46 return (Integer) bootstrapIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setBootstrapIndex(int val) {
53 try {
54 bootstrapIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getNameAndTypeIndex() {
61 try {
62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setNameAndTypeIndex(int val) {
69 try {
70 nameAndTypeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
deleted file mode 100644
index 058bb45..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
+++ /dev/null
@@ -1,75 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class MemberRefInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field classIndex;
20 private static Field nameAndTypeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MemberrefInfo");
25 classIndex = clazz.getDeclaredField("classIndex");
26 classIndex.setAccessible(true);
27 nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex");
28 nameAndTypeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public MemberRefInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getClassIndex() {
45 try {
46 return (Integer) classIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setClassIndex(int val) {
53 try {
54 classIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getNameAndTypeIndex() {
61 try {
62 return (Integer) nameAndTypeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setNameAndTypeIndex(int val) {
69 try {
70 nameAndTypeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
deleted file mode 100644
index 985e792..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
+++ /dev/null
@@ -1,75 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class MethodHandleInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field kindIndex;
20 private static Field indexIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.MethodHandleInfo");
25 kindIndex = clazz.getDeclaredField("refKind");
26 kindIndex.setAccessible(true);
27 indexIndex = clazz.getDeclaredField("refIndex");
28 indexIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public MethodHandleInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getTypeIndex() {
45 try {
46 return (Integer) kindIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setTypeIndex(int val) {
53 try {
54 kindIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getMethodRefIndex() {
61 try {
62 return (Integer) indexIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setMethodRefIndex(int val) {
69 try {
70 indexIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
deleted file mode 100644
index 10b0cb0..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
+++ /dev/null
@@ -1,57 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class MethodTypeInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field descriptorIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.MethodTypeInfo");
24 descriptorIndex = clazz.getDeclaredField("descriptor");
25 descriptorIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public MethodTypeInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getTypeIndex() {
42 try {
43 return (Integer) descriptorIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setTypeIndex(int val) {
50 try {
51 descriptorIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56
57}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
deleted file mode 100644
index cc7fdbe..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
+++ /dev/null
@@ -1,75 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class NameAndTypeInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field nameIndex;
20 private static Field typeIndex;
21
22 static {
23 try {
24 clazz = Class.forName("javassist.bytecode.NameAndTypeInfo");
25 nameIndex = clazz.getDeclaredField("memberName");
26 nameIndex.setAccessible(true);
27 typeIndex = clazz.getDeclaredField("typeDescriptor");
28 typeIndex.setAccessible(true);
29 } catch (Exception ex) {
30 throw new Error(ex);
31 }
32 }
33
34 private Object item;
35
36 public NameAndTypeInfoAccessor(Object item) {
37 this.item = item;
38 }
39
40 public static boolean isType(ConstInfoAccessor accessor) {
41 return clazz.isAssignableFrom(accessor.getItem().getClass());
42 }
43
44 public int getNameIndex() {
45 try {
46 return (Integer) nameIndex.get(this.item);
47 } catch (Exception ex) {
48 throw new Error(ex);
49 }
50 }
51
52 public void setNameIndex(int val) {
53 try {
54 nameIndex.set(this.item, val);
55 } catch (Exception ex) {
56 throw new Error(ex);
57 }
58 }
59
60 public int getTypeIndex() {
61 try {
62 return (Integer) typeIndex.get(this.item);
63 } catch (Exception ex) {
64 throw new Error(ex);
65 }
66 }
67
68 public void setTypeIndex(int val) {
69 try {
70 typeIndex.set(this.item, val);
71 } catch (Exception ex) {
72 throw new Error(ex);
73 }
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
deleted file mode 100644
index 5c68d4a..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
+++ /dev/null
@@ -1,56 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14import java.lang.reflect.Field;
15
16public class StringInfoAccessor {
17
18 private static Class<?> clazz;
19 private static Field stringIndex;
20
21 static {
22 try {
23 clazz = Class.forName("javassist.bytecode.StringInfo");
24 stringIndex = clazz.getDeclaredField("string");
25 stringIndex.setAccessible(true);
26 } catch (Exception ex) {
27 throw new Error(ex);
28 }
29 }
30
31 private Object item;
32
33 public StringInfoAccessor(Object item) {
34 this.item = item;
35 }
36
37 public static boolean isType(ConstInfoAccessor accessor) {
38 return clazz.isAssignableFrom(accessor.getItem().getClass());
39 }
40
41 public int getStringIndex() {
42 try {
43 return (Integer) stringIndex.get(this.item);
44 } catch (Exception ex) {
45 throw new Error(ex);
46 }
47 }
48
49 public void setStringIndex(int val) {
50 try {
51 stringIndex.set(this.item, val);
52 } catch (Exception ex) {
53 throw new Error(ex);
54 }
55 }
56}
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
deleted file mode 100644
index cc3b41b..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
+++ /dev/null
@@ -1,29 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.accessors;
13
14public class Utf8InfoAccessor {
15
16 private static Class<?> clazz;
17
18 static {
19 try {
20 clazz = Class.forName("javassist.bytecode.Utf8Info");
21 } catch (Exception ex) {
22 throw new Error(ex);
23 }
24 }
25
26 public static boolean isType(ConstInfoAccessor accessor) {
27 return clazz.isAssignableFrom(accessor.getItem().getClass());
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java
deleted file mode 100644
index 4ac5a8b..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java
+++ /dev/null
@@ -1,161 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.translators;
13
14import cuchaz.enigma.bytecode.ClassRenamer;
15import cuchaz.enigma.bytecode.ConstPoolEditor;
16import cuchaz.enigma.mapping.*;
17import javassist.CtBehavior;
18import javassist.CtClass;
19import javassist.CtField;
20import javassist.CtMethod;
21import javassist.bytecode.*;
22
23public class ClassTranslator {
24
25 public static void translate(Translator translator, CtClass c) {
26
27 // NOTE: the order of these translations is very important
28
29 // translate all the field and method references in the code by editing the constant pool
30 ConstPool constants = c.getClassFile().getConstPool();
31 ConstPoolEditor editor = new ConstPoolEditor(constants);
32 for (int i = 1; i < constants.getSize(); i++) {
33 switch (constants.getTag(i)) {
34
35 case ConstPool.CONST_Fieldref: {
36
37 // translate the name and type
38 FieldEntry entry = EntryFactory.getFieldEntry(
39 Descriptor.toJvmName(constants.getFieldrefClassName(i)),
40 constants.getFieldrefName(i),
41 constants.getFieldrefType(i)
42 );
43 FieldEntry translatedEntry = translator.translateEntry(entry);
44 if (!entry.equals(translatedEntry)) {
45 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString());
46 }
47 }
48 break;
49
50 case ConstPool.CONST_Methodref:
51 case ConstPool.CONST_InterfaceMethodref: {
52
53 // translate the name and type (ie signature)
54 BehaviorEntry entry = EntryFactory.getBehaviorEntry(
55 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
56 editor.getMemberrefName(i),
57 editor.getMemberrefType(i)
58 );
59 BehaviorEntry translatedEntry = translator.translateEntry(entry);
60 if (!entry.equals(translatedEntry)) {
61 editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString());
62 }
63 }
64 break;
65 default:
66 break;
67 }
68 }
69
70 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
71 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
72 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
73 ClassRenamer.applyModifier(c, modifier);
74
75 // translate all the fields
76 for (CtField field : c.getDeclaredFields()) {
77
78 // translate the name
79 FieldEntry entry = EntryFactory.getFieldEntry(field);
80 String translatedName = translator.translate(entry);
81 modifier = translator.getModifier(entry);
82 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
83 ClassRenamer.applyModifier(field, modifier);
84
85 if (translatedName != null) {
86 field.setName(translatedName);
87 }
88
89 // translate the type
90 Type translatedType = translator.translateType(entry.getType());
91 field.getFieldInfo().setDescriptor(translatedType.toString());
92 }
93
94 // translate all the methods and constructors
95 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
96
97 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
98
99 modifier = translator.getModifier(entry);
100 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
101 ClassRenamer.applyModifier(behavior, modifier);
102
103 if (behavior instanceof CtMethod) {
104 CtMethod method = (CtMethod) behavior;
105
106 // translate the name
107 String translatedName = translator.translate(entry);
108 if (translatedName != null) {
109 method.setName(translatedName);
110 }
111 }
112
113 if (entry.getSignature() != null) {
114 // translate the signature
115 Signature translatedSignature = translator.translateSignature(entry.getSignature());
116 behavior.getMethodInfo().setDescriptor(translatedSignature.toString());
117 }
118 }
119
120 // translate the EnclosingMethod attribute
121 EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag);
122 if (enclosingMethodAttr != null) {
123
124 if (enclosingMethodAttr.methodIndex() == 0) {
125 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className()));
126 BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
127 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
128 constants,
129 deobfBehaviorEntry.getClassName()
130 ));
131 } else {
132 BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(
133 Descriptor.toJvmName(enclosingMethodAttr.className()),
134 enclosingMethodAttr.methodName(),
135 enclosingMethodAttr.methodDescriptor()
136 );
137 BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry);
138 c.getClassFile().addAttribute(new EnclosingMethodAttribute(
139 constants,
140 deobfBehaviorEntry.getClassName(),
141 deobfBehaviorEntry.getName(),
142 deobfBehaviorEntry.getSignature().toString()
143 ));
144 }
145 }
146
147 // translate all the class names referenced in the code
148 // the above code only changed method/field/reference names and types, but not the rest of the class references
149 ClassRenamer.renameClasses(c, translator);
150
151 // translate the source file attribute too
152 ClassEntry deobfClassEntry = translator.translateEntry(classEntry);
153 if (deobfClassEntry != null) {
154 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
155 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
156 }
157 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
158 if (attr != null)
159 InnerClassWriter.changeModifier(c, attr, translator);
160 }
161}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
deleted file mode 100644
index 0e35938..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java
+++ /dev/null
@@ -1,144 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.translators;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.bytecode.ClassRenamer;
17import cuchaz.enigma.mapping.*;
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.*;
22
23import java.util.Collection;
24import java.util.List;
25
26public class InnerClassWriter {
27
28 // FIXME: modifier is not applied to inner class
29 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) {
30 ClassPool pool = c.getClassPool();
31 for (int i = 0; i < attr.tableLength(); i++) {
32
33 String innerName = attr.innerClass(i);
34 // get the inner class full name (which has already been translated)
35 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
36 try {
37 CtClass innerClass = pool.get(innerName);
38 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
39 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
40 ClassRenamer.applyModifier(innerClass, modifier);
41 } catch (NotFoundException e) {
42 // This shouldn't be possible in theory
43 //e.printStackTrace();
44 }
45 }
46 }
47
48 public static void write(JarIndex index, CtClass c) {
49
50 // don't change anything if there's already an attribute there
51 InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
52 if (oldAttr != null) {
53 // bail!
54 return;
55 }
56
57 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
58 List<ClassEntry> obfClassChain = index.getObfClassChain(obfClassEntry);
59
60 boolean isInnerClass = obfClassChain.size() > 1;
61 if (isInnerClass) {
62
63 // it's an inner class, rename it to the fully qualified name
64 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName());
65
66 BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry);
67 if (caller != null) {
68
69 // write the enclosing method attribute
70 if (caller.getName().equals("<clinit>")) {
71 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName()));
72 } else {
73 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString()));
74 }
75 }
76 }
77
78 // does this class have any inner classes?
79 Collection<ClassEntry> obfInnerClassEntries = index.getInnerClasses(obfClassEntry);
80
81 if (isInnerClass || !obfInnerClassEntries.isEmpty()) {
82
83 // create an inner class attribute
84 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool());
85 c.getClassFile().addAttribute(attr);
86
87 // write the ancestry, but not the outermost class
88 for (int i = 1; i < obfClassChain.size(); i++) {
89 ClassEntry obfInnerClassEntry = obfClassChain.get(i);
90 writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry);
91
92 // update references to use the fully qualified inner class name
93 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName());
94 }
95
96 // write the inner classes
97 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) {
98
99 // extend the class chain
100 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain);
101 extendedObfClassChain.add(obfInnerClassEntry);
102
103 writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry);
104
105 // update references to use the fully qualified inner class name
106 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName());
107 }
108 }
109 }
110
111 private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
112
113 // get the new inner class name
114 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain);
115 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry();
116
117 // here's what the JVM spec says about the InnerClasses attribute
118 // append(inner, parent, 0 if anonymous else simple name, flags);
119
120 // update the attribute with this inner class
121 ConstPool constPool = attr.getConstPool();
122 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName());
123 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName());
124 int innerClassNameIndex = 0;
125 int accessFlags = AccessFlag.PUBLIC;
126 // TODO: need to figure out if we can put static or not
127 if (!index.isAnonymousClass(obfClassEntry)) {
128 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName());
129 }
130
131 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
132
133 /* DEBUG
134 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)",
135 obfClassEntry,
136 attr.innerClass(attr.tableLength() - 1),
137 attr.outerClass(attr.tableLength() - 1),
138 attr.innerName(attr.tableLength() - 1),
139 Constants.NonePackage + "/" + obfInnerClassName,
140 obfClassEntry.getName()
141 ));
142 */
143 }
144}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java
deleted file mode 100644
index 51b3d2d..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java
+++ /dev/null
@@ -1,142 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.translators;
13
14import cuchaz.enigma.mapping.*;
15import javassist.CtBehavior;
16import javassist.CtClass;
17import javassist.bytecode.*;
18
19public class LocalVariableTranslator {
20
21 public static void translate(Translator translator, CtClass c) {
22 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
23
24 // if there's a local variable table, just rename everything to v1, v2, v3, ... for now
25 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
26 if (codeAttribute == null) {
27 continue;
28 }
29
30 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
31 ConstPool constants = c.getClassFile().getConstPool();
32
33 LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
34 if (table != null) {
35 renameLVT(translator, behaviorEntry, constants, table, c);
36 }
37
38 LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag);
39 if (typeTable != null) {
40 renameLVTT(typeTable, table);
41 }
42 }
43 }
44
45 // DEBUG
46 @SuppressWarnings("unused")
47 private static void dumpTable(LocalVariableAttribute table) {
48 for (int i = 0; i < table.tableLength(); i++) {
49 System.out.println(String.format("\t%d (%d): %s %s",
50 i, table.index(i), table.variableName(i), table.descriptor(i)
51 ));
52 }
53 }
54
55 private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) {
56
57 // skip empty tables
58 if (table.tableLength() <= 0) {
59 return;
60 }
61
62 // where do we start counting variables?
63 int starti = 0;
64 if (table.variableName(0).equals("this")) {
65 // skip the "this" variable
66 starti++;
67 }
68
69 // rename method arguments first
70 int numArgs = 0;
71 if (behaviorEntry.getSignature() != null) {
72 numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
73 boolean isNestedClassConstructor = false;
74
75 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
76 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) {
77 // Get the first arg type
78 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
79
80 // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
81 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
82 isNestedClassConstructor = true;
83 numArgs--;
84 }
85 }
86
87 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
88 int argi = i - starti;
89 if (ctClass.isEnum())
90 argi += 2;
91 String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
92 if (argName == null) {
93 int argIndex = isNestedClassConstructor ? argi + 1 : argi;
94 if (ctClass.isEnum())
95 argIndex -= 2;
96 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex);
97 // Unfortunately each of these have different name getters, so they have different code paths
98 if (argType.isPrimitive()) {
99 Type.Primitive argCls = argType.getPrimitive();
100 argName = "a" + argCls.name() + (argIndex + 1);
101 } else if (argType.isArray()) {
102 // List types would require this whole block again, so just go with aListx
103 argName = "aList" + (argIndex + 1);
104 } else if (argType.isClass()) {
105 ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry());
106 argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1);
107 } else {
108 argName = "a" + (argIndex + 1);
109 }
110 }
111 renameVariable(table, i, constants.addUtf8Info(argName));
112 }
113 }
114
115 // then rename the rest of the args, if any
116 for (int i = starti + numArgs; i < table.tableLength(); i++) {
117 int firstIndex = Math.min(table.index(starti + numArgs), table.index(i));
118 renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1)));
119 }
120 }
121
122 private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) {
123 // rename args to the same names as in the LVT
124 for (int i = 0; i < typeTable.tableLength(); i++) {
125 renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i)));
126 }
127 }
128
129 private static void renameVariable(LocalVariableAttribute table, int i, int stringId) {
130 // based off of LocalVariableAttribute.nameIndex()
131 ByteArray.write16bit(stringId, table.get(), i * 10 + 6);
132 }
133
134 private static int getNameIndex(LocalVariableAttribute table, int index) {
135 for (int i = 0; i < table.tableLength(); i++) {
136 if (table.index(i) == index) {
137 return table.nameIndex(i);
138 }
139 }
140 return 0;
141 }
142}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
deleted file mode 100644
index 4e632b9..0000000
--- a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java
+++ /dev/null
@@ -1,62 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.translators;
13
14import cuchaz.enigma.bytecode.MethodParametersAttribute;
15import cuchaz.enigma.mapping.*;
16import javassist.CtBehavior;
17import javassist.CtClass;
18import javassist.bytecode.CodeAttribute;
19import javassist.bytecode.LocalVariableAttribute;
20
21import java.util.ArrayList;
22import java.util.List;
23
24public class MethodParameterTranslator {
25
26 public static void translate(Translator translator, CtClass c) {
27
28 // Procyon will read method arguments from the "MethodParameters" attribute, so write those
29 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
30
31 // if there's a local variable table here, don't write a MethodParameters attribute
32 // let the local variable writer deal with it instead
33 // procyon starts doing really weird things if we give it both attributes
34 CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
35 if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) {
36 continue;
37 }
38
39 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
40
41 // get the number of arguments
42 Signature signature = behaviorEntry.getSignature();
43 if (signature == null) {
44 // static initializers have no signatures, or arguments
45 continue;
46 }
47 int numParams = signature.getArgumentTypes().size();
48 if (numParams <= 0) {
49 continue;
50 }
51
52 // get the list of argument names
53 List<String> names = new ArrayList<>(numParams);
54 for (int i = 0; i < numParams; i++) {
55 names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, "")));
56 }
57
58 // save the mappings to the class
59 MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names);
60 }
61 }
62}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
new file mode 100644
index 0000000..24bfe24
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.bytecode.translators;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.*;
17import org.objectweb.asm.*;
18
19import java.util.regex.Pattern;
20
21public class TranslationClassVisitor extends ClassVisitor {
22 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
23
24 private final Translator translator;
25 private final JarIndex jarIndex;
26 private final ReferencedEntryPool entryPool;
27
28 private ClassDefEntry obfClassEntry;
29
30 public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) {
31 super(api, cv);
32 this.translator = translator;
33 this.jarIndex = jarIndex;
34 this.entryPool = entryPool;
35 }
36
37 @Override
38 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
39 if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) {
40 signature = signature.replaceAll(":Ljava/lang/Object;:", "::");
41 }
42 obfClassEntry = new ClassDefEntry(name, new AccessFlags(access));
43 ClassDefEntry entry = translator.getTranslatedClassDef(obfClassEntry);
44 ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName));
45 String[] translatedInterfaces = new String[interfaces.length];
46 for (int i = 0; i < interfaces.length; i++) {
47 translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName();
48 }
49 super.visit(version, entry.getAccess().getFlags(), entry.getName(), signature, superEntry.getName(), translatedInterfaces);
50 }
51
52 @Override
53 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
54 FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), new AccessFlags(access));
55 FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry);
56 return super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), signature, value);
57 }
58
59 @Override
60 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
61 MethodDefEntry entry = new MethodDefEntry(obfClassEntry, name, new MethodDescriptor(desc), new AccessFlags(access));
62 MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry);
63 if (jarIndex.getBridgedMethod(entry) != null) {
64 translatedEntry.getAccess().setBridged();
65 }
66 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), name, desc, signature, exceptions);
67 return new TranslationMethodVisitor(translator, translatedEntry, api, mv);
68 }
69
70 @Override
71 public void visitOuterClass(String owner, String name, String desc) {
72 if (name != null) {
73 ClassEntry ownerEntry = translator.getTranslatedClass(entryPool.getClass(owner));
74 ClassEntry entry = translator.getTranslatedClass(entryPool.getClass(name));
75 String translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString();
76 super.visitOuterClass(ownerEntry.getName(), entry.getName(), translatedDesc);
77 } else {
78 super.visitOuterClass(owner, name, desc);
79 }
80 }
81
82 @Override
83 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
84 // TODO: Implement
85 return super.visitAnnotation(desc, visible);
86 }
87
88 @Override
89 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
90 // TODO: Implement
91 return super.visitTypeAnnotation(typeRef, typePath, desc, visible);
92 }
93
94 @Override
95 public void visitInnerClass(String name, String outerName, String innerName, int access) {
96 // If this is not an anonymous class
97 if (innerName != null && outerName != null) {
98 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(innerName, new AccessFlags(access)));
99 ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(outerName));
100 ClassEntry innerEntry = translator.getTranslatedClass(entryPool.getClass(innerName));
101 super.visitInnerClass(translatedEntry.getName(), outerEntry.getName(), innerEntry.getName(), translatedEntry.getAccess().getFlags());
102 } else {
103 super.visitInnerClass(name, outerName, innerName, access);
104 }
105 }
106}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
new file mode 100644
index 0000000..e40becc
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -0,0 +1,82 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.*;
4import org.objectweb.asm.*;
5
6import java.util.Locale;
7
8public class TranslationMethodVisitor extends MethodVisitor {
9 private final MethodDefEntry methodEntry;
10 private final Translator translator;
11
12 public TranslationMethodVisitor(Translator translator, MethodDefEntry methodEntry, int api, MethodVisitor mv) {
13 super(api, mv);
14 this.translator = translator;
15 this.methodEntry = methodEntry;
16 }
17
18 @Override
19 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
20 FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc));
21 FieldEntry translatedEntry = translator.getTranslatedField(entry);
22 if (translatedEntry != null) {
23 super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
24 } else {
25 super.visitFieldInsn(opcode, owner, name, desc);
26 }
27 }
28
29 @Override
30 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
31 MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc));
32 MethodEntry translatedEntry = translator.getTranslatedMethod(entry);
33 if (translatedEntry != null) {
34 super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf);
35 } else {
36 super.visitMethodInsn(opcode, owner, name, desc, itf);
37 }
38 }
39
40 @Override
41 public void visitAttribute(Attribute attr) {
42 // TODO: Implement
43 super.visitAttribute(attr);
44 }
45
46 @Override
47 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
48 // TODO: Implement
49 return super.visitAnnotation(desc, visible);
50 }
51
52 @Override
53 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
54 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, new TypeDescriptor(desc));
55 LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry);
56 String translatedName = translatedEntry.getName();
57 // TODO: Better name inference
58 if (translatedName.equals(entry.getName())) {
59 TypeDescriptor argDesc = translatedEntry.getDesc();
60 int nameIndex = translatedEntry.getNamedIndex() + 1;
61 String prefix = translatedEntry.getNamedIndex() < methodEntry.getDesc().getArgumentDescs().size() ? "a" : "v";
62 StringBuilder nameBuilder = new StringBuilder(prefix);
63 // Unfortunately each of these have different name getters, so they have different code paths
64 if (argDesc.isPrimitive()) {
65 TypeDescriptor.Primitive argCls = argDesc.getPrimitive();
66 nameBuilder.append(argCls.name());
67 } else if (argDesc.isArray()) {
68 // List types would require this whole block again, so just go with aListx
69 nameBuilder.append(nameIndex);
70 } else if (argDesc.isType()) {
71 String typeName = argDesc.getOwnerEntry().getSimpleName().replace("$", "");
72 typeName = typeName.substring(0, 1).toUpperCase(Locale.ROOT) + typeName.substring(1);
73 nameBuilder.append(typeName);
74 }
75 if (methodEntry.getDesc().getArgumentDescs().size() > 1) {
76 nameBuilder.append(nameIndex);
77 }
78 translatedName = nameBuilder.toString();
79 }
80 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), signature, start, end, index);
81 }
82}
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
index f76dc89..97deaf3 100644
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java
@@ -162,7 +162,7 @@ public class CodeReader extends JEditorPane {
162 // couldn't find the class declaration token, might be an anonymous class 162 // couldn't find the class declaration token, might be an anonymous class
163 // look for any declaration in that class instead 163 // look for any declaration in that class instead
164 for (Entry entry : sourceIndex.declarations()) { 164 for (Entry entry : sourceIndex.declarations()) {
165 if (entry.getClassEntry().equals(classEntry)) { 165 if (entry.getOwnerClassEntry().equals(classEntry)) {
166 token = sourceIndex.getDeclarationToken(entry); 166 token = sourceIndex.getDeclarationToken(entry);
167 break; 167 break;
168 } 168 }
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 4a891cf..5d9f7cb 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -35,7 +35,6 @@ import cuchaz.enigma.mapping.*;
35import cuchaz.enigma.throwables.IllegalNameException; 35import cuchaz.enigma.throwables.IllegalNameException;
36import cuchaz.enigma.utils.Utils; 36import cuchaz.enigma.utils.Utils;
37import de.sciss.syntaxpane.DefaultSyntaxKit; 37import de.sciss.syntaxpane.DefaultSyntaxKit;
38import javassist.bytecode.Descriptor;
39 38
40import javax.swing.*; 39import javax.swing.*;
41import javax.swing.text.BadLocationException; 40import javax.swing.text.BadLocationException;
@@ -48,8 +47,10 @@ import java.awt.*;
48import java.awt.event.*; 47import java.awt.event.*;
49import java.io.File; 48import java.io.File;
50import java.io.IOException; 49import java.io.IOException;
51import java.util.*; 50import java.util.Collection;
51import java.util.Collections;
52import java.util.List; 52import java.util.List;
53import java.util.Vector;
53import java.util.function.Function; 54import java.util.function.Function;
54 55
55public class Gui { 56public class Gui {
@@ -438,14 +439,10 @@ public class Gui {
438 showFieldEntry((FieldEntry) this.reference.entry); 439 showFieldEntry((FieldEntry) this.reference.entry);
439 } else if (this.reference.entry instanceof MethodEntry) { 440 } else if (this.reference.entry instanceof MethodEntry) {
440 showMethodEntry((MethodEntry) this.reference.entry); 441 showMethodEntry((MethodEntry) this.reference.entry);
441 } else if (this.reference.entry instanceof ConstructorEntry) {
442 showConstructorEntry((ConstructorEntry) this.reference.entry);
443 } else if (this.reference.entry instanceof ArgumentEntry) {
444 showArgumentEntry((ArgumentEntry) this.reference.entry);
445 } else if (this.reference.entry instanceof LocalVariableEntry) { 442 } else if (this.reference.entry instanceof LocalVariableEntry) {
446 showLocalVariableEntry((LocalVariableEntry) this.reference.entry); 443 showLocalVariableEntry((LocalVariableEntry) this.reference.entry);
447 } else { 444 } else {
448 throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); 445 throw new Error("Unknown entry desc: " + this.reference.entry.getClass().getName());
449 } 446 }
450 447
451 redraw(); 448 redraw();
@@ -453,10 +450,9 @@ public class Gui {
453 450
454 private void showLocalVariableEntry(LocalVariableEntry entry) { 451 private void showLocalVariableEntry(LocalVariableEntry entry) {
455 addNameValue(infoPanel, "Variable", entry.getName()); 452 addNameValue(infoPanel, "Variable", entry.getName());
456 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 453 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName());
457 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); 454 addNameValue(infoPanel, "Method", entry.getOwnerEntry().getName());
458 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); 455 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
459 addNameValue(infoPanel, "Type", entry.getType().toString());
460 } 456 }
461 457
462 private void showClassEntry(ClassEntry entry) { 458 private void showClassEntry(ClassEntry entry) {
@@ -466,32 +462,20 @@ public class Gui {
466 462
467 private void showFieldEntry(FieldEntry entry) { 463 private void showFieldEntry(FieldEntry entry) {
468 addNameValue(infoPanel, "Field", entry.getName()); 464 addNameValue(infoPanel, "Field", entry.getName());
469 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 465 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName());
470 addNameValue(infoPanel, "Type", entry.getType().toString()); 466 addNameValue(infoPanel, "TypeDescriptor", entry.getDesc().toString());
471 addModifierComboBox(infoPanel, "Modifier", entry); 467 addModifierComboBox(infoPanel, "Modifier", entry);
472 } 468 }
473 469
474 private void showMethodEntry(MethodEntry entry) { 470 private void showMethodEntry(MethodEntry entry) {
475 addNameValue(infoPanel, "Method", entry.getName()); 471 if (entry.isConstructor()) {
476 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 472 addNameValue(infoPanel, "Constructor", entry.getOwnerClassEntry().getName());
477 addNameValue(infoPanel, "Signature", entry.getSignature().toString()); 473 } else {
478 addModifierComboBox(infoPanel, "Modifier", entry); 474 addNameValue(infoPanel, "Method", entry.getName());
479 475 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName());
480 }
481
482 private void showConstructorEntry(ConstructorEntry entry) {
483 addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName());
484 if (!entry.isStatic()) {
485 addNameValue(infoPanel, "Signature", entry.getSignature().toString());
486 addModifierComboBox(infoPanel, "Modifier", entry);
487 } 476 }
488 } 477 addNameValue(infoPanel, "MethodDescriptor", entry.getDesc().toString());
489 478 addModifierComboBox(infoPanel, "Modifier", entry);
490 private void showArgumentEntry(ArgumentEntry entry) {
491 addNameValue(infoPanel, "Argument", entry.getName());
492 addNameValue(infoPanel, "Class", entry.getClassEntry().getName());
493 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName());
494 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
495 } 479 }
496 480
497 private void addNameValue(JPanel container, String name, String value) { 481 private void addNameValue(JPanel container, String name, String value) {
@@ -532,8 +516,8 @@ public class Gui {
532 reference = this.controller.getDeobfReference(token); 516 reference = this.controller.getDeobfReference(token);
533 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; 517 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry;
534 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; 518 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry;
535 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; 519 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry && !((MethodEntry) reference.entry).isConstructor();
536 boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; 520 boolean isConstructorEntry = isToken && reference.entry instanceof MethodEntry && ((MethodEntry) reference.entry).isConstructor();
537 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); 521 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry);
538 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); 522 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference);
539 523
@@ -710,16 +694,13 @@ public class Gui {
710 if (reference.entry instanceof ClassEntry) { 694 if (reference.entry instanceof ClassEntry) {
711 // look for calls to the default constructor 695 // look for calls to the default constructor
712 // TODO: get a list of all the constructors and find calls to all of them 696 // TODO: get a list of all the constructors and find calls to all of them
713 BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); 697 MethodReferenceTreeNode node = this.controller.getMethodReferences(new MethodEntry((ClassEntry) reference.entry, "<init>", new MethodDescriptor("()V")));
714 callsTree.setModel(new DefaultTreeModel(node)); 698 callsTree.setModel(new DefaultTreeModel(node));
715 } else if (reference.entry instanceof FieldEntry) { 699 } else if (reference.entry instanceof FieldEntry) {
716 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); 700 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry);
717 callsTree.setModel(new DefaultTreeModel(node)); 701 callsTree.setModel(new DefaultTreeModel(node));
718 } else if (reference.entry instanceof MethodEntry) { 702 } else if (reference.entry instanceof MethodEntry) {
719 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); 703 MethodReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry);
720 callsTree.setModel(new DefaultTreeModel(node));
721 } else if (reference.entry instanceof ConstructorEntry) {
722 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry);
723 callsTree.setModel(new DefaultTreeModel(node)); 704 callsTree.setModel(new DefaultTreeModel(node));
724 } 705 }
725 706
@@ -790,7 +771,6 @@ public class Gui {
790 // package rename 771 // package rename
791 if (data instanceof String) { 772 if (data instanceof String) {
792 for (int i = 0; i < node.getChildCount(); i++) { 773 for (int i = 0; i < node.getChildCount(); i++) {
793 data = Descriptor.toJvmName((String) data);
794 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); 774 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
795 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 775 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
796 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 776 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
@@ -807,15 +787,15 @@ public class Gui {
807 } 787 }
808 788
809 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) { 789 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) {
810 String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); 790 String oldEntry = deobfReference.entry.getOwnerClassEntry().getPackageName();
811 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); 791 String newEntry = new ClassEntry(newName).getPackageName();
812 moveClassTree(deobfReference, newName, oldEntry == null, 792 moveClassTree(deobfReference, newName, oldEntry == null,
813 newEntry == null); 793 newEntry == null);
814 } 794 }
815 795
816 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { 796 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) {
817 ClassEntry oldEntry = deobfReference.entry.getClassEntry(); 797 ClassEntry oldEntry = deobfReference.entry.getOwnerClassEntry();
818 ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); 798 ClassEntry newEntry = new ClassEntry(newName);
819 799
820 // Ob -> deob 800 // Ob -> deob
821 if (isOldOb && !isNewOb) { 801 if (isOldOb && !isNewOb) {
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 6d98743..a5528a7 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -51,7 +51,7 @@ public class GuiController {
51 return this.isDirty; 51 return this.isDirty;
52 } 52 }
53 53
54 public void openJar(final JarFile jar) { 54 public void openJar(final JarFile jar) throws IOException {
55 this.gui.onStartOpenJar(); 55 this.gui.onStartOpenJar();
56 this.deobfuscator = new Deobfuscator(jar); 56 this.deobfuscator = new Deobfuscator(jar);
57 this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); 57 this.gui.onFinishOpenJar(this.deobfuscator.getJarName());
@@ -161,24 +161,24 @@ public class GuiController {
161 161
162 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { 162 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
163 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 163 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
164 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 164 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry);
165 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); 165 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry);
166 } 166 }
167 167
168 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { 168 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) {
169 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 169 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
170 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 170 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry);
171 } 171 }
172 172
173 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { 173 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) {
174 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 174 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
175 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 175 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
176 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); 176 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry);
177 } 177 }
178 178
179 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { 179 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) {
180 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 180 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
181 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 181 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
182 if (rootNodes.isEmpty()) { 182 if (rootNodes.isEmpty()) {
183 return null; 183 return null;
184 } 184 }
@@ -190,14 +190,14 @@ public class GuiController {
190 190
191 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { 191 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) {
192 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); 192 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry);
193 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); 193 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfFieldEntry);
194 rootNode.load(this.deobfuscator.getJarIndex(), true); 194 rootNode.load(this.deobfuscator.getJarIndex(), true);
195 return rootNode; 195 return rootNode;
196 } 196 }
197 197
198 public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { 198 public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry) {
199 BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); 199 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
200 BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); 200 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
201 rootNode.load(this.deobfuscator.getJarIndex(), true); 201 rootNode.load(this.deobfuscator.getJarIndex(), true);
202 return rootNode; 202 return rootNode;
203 } 203 }
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index f80abba..caa985c 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -11,8 +11,6 @@
11 11
12package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
13 13
14import javassist.bytecode.Descriptor;
15
16import javax.swing.tree.DefaultMutableTreeNode; 14import javax.swing.tree.DefaultMutableTreeNode;
17 15
18public class ClassSelectorPackageNode extends DefaultMutableTreeNode { 16public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
@@ -41,7 +39,7 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
41 39
42 @Override 40 @Override
43 public String toString() { 41 public String toString() {
44 return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; 42 return !packageName.equals("(none)") ? this.packageName : "(none)";
45 } 43 }
46 44
47 @Override 45 @Override
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
deleted file mode 100644
index 9154cc2..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
+++ /dev/null
@@ -1,110 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.utils.Utils;
15
16public class ArgumentEntry implements Entry {
17
18 private BehaviorEntry behaviorEntry;
19 private int index;
20 private String name;
21
22 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) {
23 if (behaviorEntry == null) {
24 throw new IllegalArgumentException("Behavior cannot be null!");
25 }
26 if (index < 0) {
27 throw new IllegalArgumentException("Index must be non-negative!");
28 }
29 if (name == null) {
30 throw new IllegalArgumentException("Argument name cannot be null!");
31 }
32
33 this.behaviorEntry = behaviorEntry;
34 this.index = index;
35 this.name = name;
36 }
37
38 public ArgumentEntry(ArgumentEntry other) {
39 this.behaviorEntry = other.getBehaviorEntry();
40 this.index = other.index;
41 this.name = other.name;
42 }
43
44 public ArgumentEntry(ArgumentEntry other, String newClassName) {
45 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName));
46 this.index = other.index;
47 this.name = other.name;
48 }
49
50 public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) {
51 this.behaviorEntry = entry;
52 this.index = other.index;
53 this.name = other.name;
54 }
55
56 public BehaviorEntry getBehaviorEntry() {
57 return this.behaviorEntry;
58 }
59
60 public int getIndex() {
61 return this.index;
62 }
63
64 @Override
65 public String getName() {
66 return this.name;
67 }
68
69 @Override
70 public ClassEntry getClassEntry() {
71 return this.behaviorEntry.getClassEntry();
72 }
73
74 @Override
75 public String getClassName() {
76 return this.behaviorEntry.getClassName();
77 }
78
79 @Override
80 public ArgumentEntry cloneToNewClass(ClassEntry classEntry) {
81 return new ArgumentEntry(this, classEntry.getName());
82 }
83
84 public String getMethodName() {
85 return this.behaviorEntry.getName();
86 }
87
88 public Signature getMethodSignature() {
89 return this.behaviorEntry.getSignature();
90 }
91
92 @Override
93 public int hashCode() {
94 return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode());
95 }
96
97 @Override
98 public boolean equals(Object other) {
99 return other instanceof ArgumentEntry && equals((ArgumentEntry) other);
100 }
101
102 public boolean equals(ArgumentEntry other) {
103 return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name);
104 }
105
106 @Override
107 public String toString() {
108 return this.behaviorEntry + "(" + this.index + ":" + this.name + ")";
109 }
110}
diff --git a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java
index 04b4ebc..dc1b02e 100644
--- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java
@@ -11,6 +11,19 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public interface BehaviorEntry extends Entry { 14import com.google.common.base.Preconditions;
15 Signature getSignature(); 15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class ClassDefEntry extends ClassEntry {
18 private final AccessFlags access;
19
20 public ClassDefEntry(String className, AccessFlags access) {
21 super(className);
22 Preconditions.checkNotNull(access, "Class access cannot be null");
23 this.access = access;
24 }
25
26 public AccessFlags getAccess() {
27 return access;
28 }
16} 29}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
index 788811f..a49f8dd 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
@@ -11,18 +11,18 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15 16
16import java.util.List; 17import java.util.List;
17 18
18public class ClassEntry implements Entry { 19public class ClassEntry implements Entry {
19 20
20 private String name; 21 private final String name;
21 22
22 public ClassEntry(String className) { 23 public ClassEntry(String className) {
23 if (className == null) { 24 Preconditions.checkNotNull(className, "Class name cannot be null");
24 throw new IllegalArgumentException("Class name cannot be null!"); 25
25 }
26 if (className.indexOf('.') >= 0) { 26 if (className.indexOf('.') >= 0) {
27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); 27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
28 } 28 }
@@ -49,12 +49,12 @@ public class ClassEntry implements Entry {
49 } 49 }
50 50
51 @Override 51 @Override
52 public ClassEntry getClassEntry() { 52 public ClassEntry getOwnerClassEntry() {
53 return this; 53 return this;
54 } 54 }
55 55
56 @Override 56 @Override
57 public ClassEntry cloneToNewClass(ClassEntry classEntry) { 57 public ClassEntry updateOwnership(ClassEntry classEntry) {
58 return classEntry; 58 return classEntry;
59 } 59 }
60 60
@@ -132,11 +132,7 @@ public class ClassEntry implements Entry {
132 } 132 }
133 133
134 public String getPackageName() { 134 public String getPackageName() {
135 int pos = this.name.lastIndexOf('/'); 135 return getPackageName(this.name);
136 if (pos > 0) {
137 return this.name.substring(0, pos);
138 }
139 return null;
140 } 136 }
141 137
142 public String getSimpleName() { 138 public String getSimpleName() {
@@ -147,6 +143,14 @@ public class ClassEntry implements Entry {
147 return this.name; 143 return this.name;
148 } 144 }
149 145
146 public static String getPackageName(String name) {
147 int pos = name.lastIndexOf('/');
148 if (pos > 0) {
149 return name.substring(0, pos);
150 }
151 return null;
152 }
153
150 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 154 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
151 assert (classChain.contains(this)); 155 assert (classChain.contains(this));
152 StringBuilder buf = new StringBuilder(); 156 StringBuilder buf = new StringBuilder();
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index 51751ca..c782250 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -34,7 +34,6 @@ public class ClassMapping implements Comparable<ClassMapping> {
34 private Map<String, MethodMapping> methodsByDeobf; 34 private Map<String, MethodMapping> methodsByDeobf;
35 private boolean isDirty; 35 private boolean isDirty;
36 private Mappings.EntryModifier modifier; 36 private Mappings.EntryModifier modifier;
37 private boolean deobfInner;
38 37
39 public ClassMapping(String obfFullName) { 38 public ClassMapping(String obfFullName) {
40 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); 39 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
@@ -81,6 +80,10 @@ public class ClassMapping implements Comparable<ClassMapping> {
81 return deobfName; 80 return deobfName;
82 } 81 }
83 82
83 public String getTranslatedName(TranslationDirection direction) {
84 return direction.choose(deobfName, obfFullName);
85 }
86
84 //// INNER CLASSES //////// 87 //// INNER CLASSES ////////
85 88
86 public void setDeobfName(String val) { 89 public void setDeobfName(String val) {
@@ -191,21 +194,21 @@ public class ClassMapping implements Comparable<ClassMapping> {
191 return fieldsByObf.values(); 194 return fieldsByObf.values();
192 } 195 }
193 196
194 public boolean containsObfField(String obfName, Type obfType) { 197 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
195 return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); 198 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
196 } 199 }
197 200
198 public boolean containsDeobfField(String deobfName, Type deobfType) { 201 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
199 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); 202 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
200 } 203 }
201 204
202 public void addFieldMapping(FieldMapping fieldMapping) { 205 public void addFieldMapping(FieldMapping fieldMapping) {
203 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 206 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
204 if (fieldsByObf.containsKey(obfKey)) { 207 if (fieldsByObf.containsKey(obfKey)) {
205 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 208 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
206 } 209 }
207 if (fieldMapping.getDeobfName() != null) { 210 if (fieldMapping.getDeobfName() != null) {
208 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 211 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
209 if (fieldsByDeobf.containsKey(deobfKey)) { 212 if (fieldsByDeobf.containsKey(deobfKey)) {
210 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 213 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
211 } 214 }
@@ -218,63 +221,67 @@ public class ClassMapping implements Comparable<ClassMapping> {
218 } 221 }
219 222
220 public void removeFieldMapping(FieldMapping fieldMapping) { 223 public void removeFieldMapping(FieldMapping fieldMapping) {
221 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; 224 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
222 assert (obfWasRemoved); 225 assert (obfWasRemoved);
223 if (fieldMapping.getDeobfName() != null) { 226 if (fieldMapping.getDeobfName() != null) {
224 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 227 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
225 assert (deobfWasRemoved); 228 assert (deobfWasRemoved);
226 } 229 }
227 this.isDirty = true; 230 this.isDirty = true;
228 } 231 }
229 232
230 public FieldMapping getFieldByObf(String obfName, Type obfType) { 233 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
231 return fieldsByObf.get(getFieldKey(obfName, obfType)); 234 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
235 }
236
237 public FieldMapping getFieldByObf(FieldEntry field) {
238 return getFieldByObf(field.getName(), field.getDesc());
232 } 239 }
233 240
234 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { 241 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
235 return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 242 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
236 } 243 }
237 244
238 public String getObfFieldName(String deobfName, Type obfType) { 245 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
239 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 246 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
240 if (fieldMapping != null) { 247 if (fieldMapping != null) {
241 return fieldMapping.getObfName(); 248 return fieldMapping.getObfName();
242 } 249 }
243 return null; 250 return null;
244 } 251 }
245 252
246 public String getDeobfFieldName(String obfName, Type obfType) { 253 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
247 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 254 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
248 if (fieldMapping != null) { 255 if (fieldMapping != null) {
249 return fieldMapping.getDeobfName(); 256 return fieldMapping.getDeobfName();
250 } 257 }
251 return null; 258 return null;
252 } 259 }
253 260
254 private String getFieldKey(String name, Type type) { 261 private String getFieldKey(String name, TypeDescriptor desc) {
255 if (name == null) { 262 if (name == null) {
256 throw new IllegalArgumentException("name cannot be null!"); 263 throw new IllegalArgumentException("name cannot be null!");
257 } 264 }
258 if (type == null) { 265 if (desc == null) {
259 throw new IllegalArgumentException("type cannot be null!"); 266 throw new IllegalArgumentException("desc cannot be null!");
260 } 267 }
261 return name + ":" + type; 268 return name + ":" + desc;
262 } 269 }
263 270
264 public void setFieldName(String obfName, Type obfType, String deobfName) { 271 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
265 assert (deobfName != null); 272 assert (deobfName != null);
266 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 273 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
267 if (fieldMapping == null) { 274 if (fieldMapping == null) {
268 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); 275 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
269 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 276 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
270 assert (obfWasAdded); 277 assert (obfWasAdded);
271 } else { 278 } else {
272 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; 279 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
273 assert (wasRemoved); 280 assert (wasRemoved);
274 } 281 }
275 fieldMapping.setDeobfName(deobfName); 282 fieldMapping.setDeobfName(deobfName);
276 if (deobfName != null) { 283 if (deobfName != null) {
277 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 284 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
278 assert (wasAdded); 285 assert (wasAdded);
279 } 286 }
280 this.isDirty = true; 287 this.isDirty = true;
@@ -282,13 +289,13 @@ public class ClassMapping implements Comparable<ClassMapping> {
282 289
283 //// METHODS //////// 290 //// METHODS ////////
284 291
285 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 292 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
286 assert (newObfName != null); 293 assert (newObfName != null);
287 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); 294 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
288 assert (fieldMapping != null); 295 assert (fieldMapping != null);
289 fieldMapping.setObfName(newObfName); 296 fieldMapping.setObfName(newObfName);
290 fieldMapping.setObfType(newObfType); 297 fieldMapping.setObfDesc(newObfDesc);
291 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 298 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
292 assert (obfWasAdded); 299 assert (obfWasAdded);
293 this.isDirty = true; 300 this.isDirty = true;
294 } 301 }
@@ -298,23 +305,23 @@ public class ClassMapping implements Comparable<ClassMapping> {
298 return methodsByObf.values(); 305 return methodsByObf.values();
299 } 306 }
300 307
301 public boolean containsObfMethod(String obfName, Signature obfSignature) { 308 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
302 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); 309 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
303 } 310 }
304 311
305 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { 312 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
306 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); 313 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
307 } 314 }
308 315
309 public void addMethodMapping(MethodMapping methodMapping) { 316 public void addMethodMapping(MethodMapping methodMapping) {
310 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 317 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
311 if (methodsByObf.containsKey(obfKey)) { 318 if (methodsByObf.containsKey(obfKey)) {
312 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 319 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
313 } 320 }
314 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; 321 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
315 assert (wasAdded); 322 assert (wasAdded);
316 if (methodMapping.getDeobfName() != null) { 323 if (methodMapping.getDeobfName() != null) {
317 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); 324 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
318 if (methodsByDeobf.containsKey(deobfKey)) { 325 if (methodsByDeobf.containsKey(deobfKey)) {
319 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 326 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
320 } 327 }
@@ -326,44 +333,48 @@ public class ClassMapping implements Comparable<ClassMapping> {
326 } 333 }
327 334
328 public void removeMethodMapping(MethodMapping methodMapping) { 335 public void removeMethodMapping(MethodMapping methodMapping) {
329 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; 336 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
330 assert (obfWasRemoved); 337 assert (obfWasRemoved);
331 if (methodMapping.getDeobfName() != null) { 338 if (methodMapping.getDeobfName() != null) {
332 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 339 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
333 assert (deobfWasRemoved); 340 assert (deobfWasRemoved);
334 } 341 }
335 this.isDirty = true; 342 this.isDirty = true;
336 } 343 }
337 344
338 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 345 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
339 return methodsByObf.get(getMethodKey(obfName, obfSignature)); 346 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
347 }
348
349 public MethodMapping getMethodByObf(MethodEntry method) {
350 return getMethodByObf(method.getName(), method.getDesc());
340 } 351 }
341 352
342 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { 353 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
343 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); 354 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
344 } 355 }
345 356
346 private String getMethodKey(String name, Signature signature) { 357 private String getMethodKey(String name, MethodDescriptor descriptor) {
347 if (name == null) { 358 if (name == null) {
348 throw new IllegalArgumentException("name cannot be null!"); 359 throw new IllegalArgumentException("name cannot be null!");
349 } 360 }
350 if (signature == null) { 361 if (descriptor == null) {
351 throw new IllegalArgumentException("signature cannot be null!"); 362 throw new IllegalArgumentException("descriptor cannot be null!");
352 } 363 }
353 return name + signature; 364 return name + descriptor;
354 } 365 }
355 366
356 public void setMethodName(String obfName, Signature obfSignature, String deobfName) { 367 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
357 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); 368 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
358 if (methodMapping == null) { 369 if (methodMapping == null) {
359 methodMapping = createMethodMapping(obfName, obfSignature); 370 methodMapping = createMethodMapping(obfName, obfDescriptor);
360 } else if (methodMapping.getDeobfName() != null) { 371 } else if (methodMapping.getDeobfName() != null) {
361 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 372 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
362 assert (wasRemoved); 373 assert (wasRemoved);
363 } 374 }
364 methodMapping.setDeobfName(deobfName); 375 methodMapping.setDeobfName(deobfName);
365 if (deobfName != null) { 376 if (deobfName != null) {
366 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 377 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
367 assert (wasAdded); 378 assert (wasAdded);
368 } 379 }
369 this.isDirty = true; 380 this.isDirty = true;
@@ -371,35 +382,35 @@ public class ClassMapping implements Comparable<ClassMapping> {
371 382
372 //// ARGUMENTS //////// 383 //// ARGUMENTS ////////
373 384
374 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 385 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
375 assert (newObfName != null); 386 assert (newObfName != null);
376 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); 387 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
377 assert (methodMapping != null); 388 assert (methodMapping != null);
378 methodMapping.setObfName(newObfName); 389 methodMapping.setObfName(newObfName);
379 methodMapping.setObfSignature(newObfSignature); 390 methodMapping.setObfDescriptor(newObfDescriptor);
380 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 391 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
381 assert (obfWasAdded); 392 assert (obfWasAdded);
382 this.isDirty = true; 393 this.isDirty = true;
383 } 394 }
384 395
385 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 396 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
386 assert (argumentName != null); 397 assert (argumentName != null);
387 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); 398 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
388 if (methodMapping == null) { 399 if (methodMapping == null) {
389 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 400 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
390 } 401 }
391 methodMapping.setArgumentName(argumentIndex, argumentName); 402 methodMapping.setLocalVariableName(argumentIndex, argumentName);
392 this.isDirty = true; 403 this.isDirty = true;
393 } 404 }
394 405
395 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 406 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
396 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 407 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
397 this.isDirty = true; 408 this.isDirty = true;
398 } 409 }
399 410
400 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 411 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
401 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 412 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
402 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 413 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
403 assert (wasAdded); 414 assert (wasAdded);
404 this.isDirty = true; 415 this.isDirty = true;
405 return methodMapping; 416 return methodMapping;
@@ -459,24 +470,24 @@ public class ClassMapping implements Comparable<ClassMapping> {
459 470
460 // rename field types 471 // rename field types
461 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { 472 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
462 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 473 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
463 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { 474 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
464 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; 475 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
465 assert (wasRemoved); 476 assert (wasRemoved);
466 boolean wasAdded = fieldsByObf 477 boolean wasAdded = fieldsByObf
467 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; 478 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
468 assert (wasAdded); 479 assert (wasAdded);
469 } 480 }
470 } 481 }
471 482
472 // rename method signatures 483 // rename method signatures
473 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { 484 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
474 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 485 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
475 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { 486 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
476 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; 487 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
477 assert (wasRemoved); 488 assert (wasRemoved);
478 boolean wasAdded = methodsByObf 489 boolean wasAdded = methodsByObf
479 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; 490 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
480 assert (wasAdded); 491 assert (wasAdded);
481 } 492 }
482 } 493 }
@@ -490,9 +501,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
490 return false; 501 return false;
491 } 502 }
492 503
493 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 504 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
494 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); 505 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
495 return methodMapping != null && methodMapping.containsArgument(name); 506 return methodMapping != null && methodMapping.containsLocalVariable(name);
496 } 507 }
497 508
498 public ClassEntry getObfEntry() { 509 public ClassEntry getObfEntry() {
@@ -521,9 +532,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
521 this.modifier = modifier; 532 this.modifier = modifier;
522 } 533 }
523 534
524 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { 535 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
525 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), 536 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
526 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); 537 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
527 538
528 if (fieldMapping.getModifier() != modifier) { 539 if (fieldMapping.getModifier() != modifier) {
529 fieldMapping.setModifier(modifier); 540 fieldMapping.setModifier(modifier);
@@ -531,7 +542,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
531 } 542 }
532 } 543 }
533 544
534 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { 545 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
535 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), 546 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
536 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); 547 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
537 548
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
deleted file mode 100644
index 801c410..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
+++ /dev/null
@@ -1,16 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14public interface ClassNameReplacer {
15 String replace(String className);
16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
deleted file mode 100644
index 20e5113..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
+++ /dev/null
@@ -1,105 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.utils.Utils;
15
16public class ConstructorEntry implements BehaviorEntry {
17
18 private ClassEntry classEntry;
19 private Signature signature;
20
21 public ConstructorEntry(ClassEntry classEntry) {
22 this(classEntry, null);
23 }
24
25 public ConstructorEntry(ClassEntry classEntry, Signature signature) {
26 if (classEntry == null) {
27 throw new IllegalArgumentException("Class cannot be null!");
28 }
29
30 this.classEntry = classEntry;
31 this.signature = signature;
32 }
33
34 public ConstructorEntry(ConstructorEntry other, String newClassName) {
35 this.classEntry = new ClassEntry(newClassName);
36 this.signature = other.signature;
37 }
38
39 @Override
40 public ClassEntry getClassEntry() {
41 return this.classEntry;
42 }
43
44 @Override
45 public String getName() {
46 if (isStatic()) {
47 return "<clinit>";
48 }
49 return "<init>";
50 }
51
52 public boolean isStatic() {
53 return this.signature == null;
54 }
55
56 @Override
57 public Signature getSignature() {
58 return this.signature;
59 }
60
61 @Override
62 public String getClassName() {
63 return this.classEntry.getName();
64 }
65
66 @Override
67 public ConstructorEntry cloneToNewClass(ClassEntry classEntry) {
68 return new ConstructorEntry(this, classEntry.getName());
69 }
70
71 @Override
72 public int hashCode() {
73 if (isStatic()) {
74 return Utils.combineHashesOrdered(this.classEntry);
75 } else {
76 return Utils.combineHashesOrdered(this.classEntry, this.signature);
77 }
78 }
79
80 @Override
81 public boolean equals(Object other) {
82 return other instanceof ConstructorEntry && equals((ConstructorEntry) other);
83 }
84
85 public boolean equals(ConstructorEntry other) {
86 if (isStatic() != other.isStatic()) {
87 return false;
88 }
89
90 if (isStatic()) {
91 return this.classEntry.equals(other.classEntry);
92 } else {
93 return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature);
94 }
95 }
96
97 @Override
98 public String toString() {
99 if (isStatic()) {
100 return this.classEntry.getName() + "." + getName();
101 } else {
102 return this.classEntry.getName() + "." + getName() + this.signature;
103 }
104 }
105}
diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
new file mode 100644
index 0000000..1283267
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
@@ -0,0 +1,319 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.bytecode.AccessFlags;
18
19import java.util.ArrayList;
20import java.util.List;
21import java.util.Map;
22
23public class DirectionalTranslator implements Translator {
24
25 private final TranslationDirection direction;
26 private final Map<String, ClassMapping> classes;
27 private final TranslationIndex index;
28
29 public DirectionalTranslator(ReferencedEntryPool entryPool) {
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex(entryPool);
33 }
34
35 public DirectionalTranslator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40
41 public TranslationDirection getDirection() {
42 return direction;
43 }
44
45 public TranslationIndex getTranslationIndex() {
46 return index;
47 }
48
49 @Override
50 public ClassEntry getTranslatedClass(ClassEntry entry) {
51 String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
52 return new ClassEntry(className);
53 }
54
55 @Override
56 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
57 String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
58 return new ClassDefEntry(className, getClassModifier(entry).transform(entry.getAccess()));
59 }
60
61 private String translateClassName(ClassEntry entry) {
62 // normal classes are easy
63 ClassMapping classMapping = this.classes.get(entry.getName());
64 if (classMapping == null) {
65 return entry.getName();
66 }
67 return classMapping.getTranslatedName(direction);
68 }
69
70 private String translateInnerClassName(ClassEntry entry) {
71 // translate as much of the class chain as we can
72 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
73 String[] obfClassNames = entry.getName().split("\\$");
74 StringBuilder buf = new StringBuilder();
75 for (int i = 0; i < obfClassNames.length; i++) {
76 boolean isFirstClass = buf.length() == 0;
77 String className = null;
78 ClassMapping classMapping = mappingsChain.get(i);
79 if (classMapping != null) {
80 className = this.direction.choose(
81 classMapping.getDeobfName(),
82 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
83 );
84 }
85 if (className == null) {
86 className = obfClassNames[i];
87 }
88 if (!isFirstClass) {
89 buf.append("$");
90 }
91 buf.append(className);
92 }
93 return buf.toString();
94 }
95
96 @Override
97 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
98 String translatedName = translateFieldName(entry);
99 if (translatedName == null) {
100 return entry;
101 }
102 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
103 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
104 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
105 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedAccess);
106 }
107
108 @Override
109 public FieldEntry getTranslatedField(FieldEntry entry) {
110 String translatedName = translateFieldName(entry);
111 if (translatedName == null) {
112 return null;
113 }
114 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
115 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
116 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
117 }
118
119 private String translateFieldName(FieldEntry entry) {
120 // resolve the class entry
121 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry);
122 if (resolvedClassEntry != null) {
123 // look for the class
124 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
125 if (classMapping != null) {
126 // look for the field
127 FieldMapping mapping = classMapping.getFieldByObf(entry.getName(), entry.getDesc());
128 if (mapping != null) {
129 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
130 }
131 }
132 }
133 return null;
134 }
135
136 @Override
137 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
138 String translatedName = translateMethodName(entry);
139 if (translatedName == null) {
140 return entry;
141 }
142 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
143 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
144 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
145 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, access);
146 }
147
148 @Override
149 public MethodEntry getTranslatedMethod(MethodEntry entry) {
150 String translatedName = translateMethodName(entry);
151 if (translatedName == null) {
152 return null;
153 }
154 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
155 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
156 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
157 }
158
159 private String translateMethodName(MethodEntry entry) {
160 // resolve the class entry
161 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry, true);
162 if (resolvedOwner != null) {
163 // look for class
164 ClassMapping classMapping = findClassMapping(resolvedOwner);
165 if (classMapping != null) {
166 // look for the method
167 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
168 if (mapping != null) {
169 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
170 }
171 }
172 }
173 return null;
174 }
175
176 @Override
177 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
178 String translatedArgumentName = translateLocalVariableName(entry);
179 if (translatedArgumentName == null) {
180 translatedArgumentName = inheritLocalVariableName(entry);
181 }
182 if (translatedArgumentName == null) {
183 return null;
184 }
185 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
186 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
187 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName);
188 }
189
190 @Override
191 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
192 String translatedArgumentName = translateLocalVariableName(entry);
193 if (translatedArgumentName == null) {
194 translatedArgumentName = inheritLocalVariableName(entry);
195 }
196 if (translatedArgumentName == null) {
197 return entry;
198 }
199 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
200 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
201 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
202 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName, translatedTypeDesc);
203 }
204
205 // TODO: support not identical behavior (specific to constructor)
206 private String translateLocalVariableName(LocalVariableEntry entry) {
207 // look for identical behavior in superclasses
208 ClassEntry ownerEntry = entry.getOwnerClassEntry();
209 if (ownerEntry != null) {
210 // look for the class
211 ClassMapping classMapping = findClassMapping(ownerEntry);
212 if (classMapping != null) {
213 // look for the method
214 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc());
215 if (methodMapping != null) {
216 int index = entry.getIndex();
217 return this.direction.choose(
218 methodMapping.getDeobfLocalVariableName(index),
219 methodMapping.getObfLocalVariableName(index)
220 );
221 }
222 }
223 }
224 return null;
225 }
226
227 private String inheritLocalVariableName(LocalVariableEntry entry) {
228 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
229 // Check in mother class for the arg
230 for (ClassEntry ancestorEntry : ancestry) {
231 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
232 if (this.index.entryExists(motherArg)) {
233 String result = translateLocalVariableName(motherArg);
234 if (result != null) {
235 return result;
236 }
237 }
238 }
239 return null;
240 }
241
242 @Override
243 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
244 return desc.remap(name -> getTranslatedClass(new ClassEntry(name)).getName());
245 }
246
247 @Override
248 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
249 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
250 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
251 for (TypeDescriptor argument : arguments) {
252 translatedArguments.add(getTranslatedTypeDesc(argument));
253 }
254 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
255 }
256
257 private ClassMapping findClassMapping(ClassEntry entry) {
258 List<ClassMapping> mappingChain = getClassMappingChain(entry);
259 return mappingChain.get(mappingChain.size() - 1);
260 }
261
262 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
263
264 // get a list of all the classes in the hierarchy
265 String[] parts = entry.getName().split("\\$");
266 List<ClassMapping> mappingsChain = Lists.newArrayList();
267
268 // get mappings for the outer class
269 ClassMapping outerClassMapping = this.classes.get(parts[0]);
270 mappingsChain.add(outerClassMapping);
271
272 for (int i = 1; i < parts.length; i++) {
273
274 // get mappings for the inner class
275 ClassMapping innerClassMapping = null;
276 if (outerClassMapping != null) {
277 innerClassMapping = this.direction.choose(
278 outerClassMapping.getInnerClassByObfSimple(parts[i]),
279 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
280 );
281 }
282 mappingsChain.add(innerClassMapping);
283 outerClassMapping = innerClassMapping;
284 }
285
286 assert (mappingsChain.size() == parts.length);
287 return mappingsChain;
288 }
289
290 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
291 ClassMapping classMapping = findClassMapping(entry);
292 if (classMapping != null) {
293 return classMapping.getModifier();
294 }
295 return Mappings.EntryModifier.UNCHANGED;
296 }
297
298 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
299 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
300 if (classMapping != null) {
301 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
302 if (fieldMapping != null) {
303 return fieldMapping.getModifier();
304 }
305 }
306 return Mappings.EntryModifier.UNCHANGED;
307 }
308
309 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
310 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
311 if (classMapping != null) {
312 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
313 if (methodMapping != null) {
314 return methodMapping.getModifier();
315 }
316 }
317 return Mappings.EntryModifier.UNCHANGED;
318 }
319}
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java
index c79510b..eb783e9 100644
--- a/src/main/java/cuchaz/enigma/mapping/Entry.java
+++ b/src/main/java/cuchaz/enigma/mapping/Entry.java
@@ -16,7 +16,7 @@ public interface Entry {
16 16
17 String getClassName(); 17 String getClassName();
18 18
19 ClassEntry getClassEntry(); 19 ClassEntry getOwnerClassEntry();
20 20
21 Entry cloneToNewClass(ClassEntry classEntry); 21 Entry updateOwnership(ClassEntry classEntry);
22} 22}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
index 993bb64..c20f6f5 100644
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
@@ -12,19 +12,8 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.analysis.JarIndex;
15import javassist.*;
16import javassist.bytecode.Descriptor;
17import javassist.expr.ConstructorCall;
18import javassist.expr.FieldAccess;
19import javassist.expr.MethodCall;
20import javassist.expr.NewExpr;
21 15
22public class EntryFactory { 16public class EntryFactory {
23
24 public static ClassEntry getClassEntry(CtClass c) {
25 return new ClassEntry(Descriptor.toJvmName(c.getName()));
26 }
27
28 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { 17 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
29 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); 18 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
30 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); 19 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
@@ -38,95 +27,19 @@ public class EntryFactory {
38 return new ClassEntry(classMapping.getDeobfName()); 27 return new ClassEntry(classMapping.getDeobfName());
39 } 28 }
40 29
41 public static ClassEntry getSuperclassEntry(CtClass c) {
42 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
43 }
44
45 public static FieldEntry getFieldEntry(CtField field) {
46 return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor()));
47 }
48
49 public static FieldEntry getFieldEntry(FieldAccess call) {
50 return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature()));
51 }
52
53 public static FieldEntry getFieldEntry(String className, String name, String type) {
54 return new FieldEntry(new ClassEntry(className), name, new Type(type));
55 }
56
57 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { 30 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
58 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); 31 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
59 }
60
61 public static MethodEntry getMethodEntry(CtMethod method) {
62 return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor()));
63 }
64
65 public static MethodEntry getMethodEntry(MethodCall call) {
66 return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature()));
67 }
68
69 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
70 if (constructor.isClassInitializer()) {
71 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()));
72 } else {
73 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor()));
74 }
75 }
76
77 public static ConstructorEntry getConstructorEntry(ConstructorCall call) {
78 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
79 }
80
81 public static ConstructorEntry getConstructorEntry(NewExpr call) {
82 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
83 }
84
85 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
86 if (behavior instanceof CtMethod) {
87 return getMethodEntry((CtMethod) behavior);
88 } else if (behavior instanceof CtConstructor) {
89 return getConstructorEntry((CtConstructor) behavior);
90 }
91 throw new Error("behavior is neither Method nor Constructor!");
92 }
93
94 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) {
95 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature));
96 }
97
98 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) {
99 return getBehaviorEntry(new ClassEntry(className), behaviorName);
100 }
101
102 public static BehaviorEntry getBehaviorEntry(String className) {
103 return new ConstructorEntry(new ClassEntry(className));
104 }
105
106 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) {
107 switch (behaviorName) {
108 case "<init>":
109 return new ConstructorEntry(classEntry, behaviorSignature);
110 case "<clinit>":
111 return new ConstructorEntry(classEntry);
112 default:
113 return new MethodEntry(classEntry, behaviorName, behaviorSignature);
114 }
115 } 32 }
116 33
117 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { 34 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
118 if (behaviorName.equals("<clinit>")) { 35 return new MethodEntry(classEntry, name, desc);
119 return new ConstructorEntry(classEntry);
120 } else {
121 throw new IllegalArgumentException("Only class initializers don't have signatures");
122 }
123 } 36 }
124 37
125 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { 38 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
126 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); 39 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
127 } 40 }
128 41
129 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { 42 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
130 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); 43 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
131 } 44 }
132} 45}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java
new file mode 100644
index 0000000..262c16c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class FieldDefEntry extends FieldEntry {
18 private final AccessFlags access;
19
20 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, AccessFlags access) {
21 super(ownerEntry, name, desc);
22 Preconditions.checkNotNull(access, "Field access cannot be null");
23 this.access = access;
24 }
25
26 public AccessFlags getAccess() {
27 return access;
28 }
29
30 @Override
31 public FieldDefEntry updateOwnership(ClassEntry owner) {
32 return new FieldDefEntry(owner, this.name, this.desc, access);
33 }
34}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
index 0f1f506..c118ac0 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
@@ -11,40 +11,29 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
15 16
16public class FieldEntry implements Entry { 17public class FieldEntry implements Entry {
17 18
18 private ClassEntry classEntry; 19 protected final ClassEntry ownerEntry;
19 private String name; 20 protected final String name;
20 private Type type; 21 protected final TypeDescriptor desc;
21 22
22 // NOTE: this argument order is important for the MethodReader/MethodWriter 23 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public FieldEntry(ClassEntry classEntry, String name, Type type) { 24 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
24 if (classEntry == null) { 25 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
25 throw new IllegalArgumentException("Class cannot be null!"); 26 Preconditions.checkNotNull(name, "Field name cannot be null");
26 } 27 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
27 if (name == null) {
28 throw new IllegalArgumentException("Field name cannot be null!");
29 }
30 if (type == null) {
31 throw new IllegalArgumentException("Field type cannot be null!");
32 }
33 28
34 this.classEntry = classEntry; 29 this.ownerEntry = ownerEntry;
35 this.name = name; 30 this.name = name;
36 this.type = type; 31 this.desc = desc;
37 }
38
39 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
40 this.classEntry = newClassEntry;
41 this.name = other.name;
42 this.type = other.type;
43 } 32 }
44 33
45 @Override 34 @Override
46 public ClassEntry getClassEntry() { 35 public ClassEntry getOwnerClassEntry() {
47 return this.classEntry; 36 return this.ownerEntry;
48 } 37 }
49 38
50 @Override 39 @Override
@@ -54,21 +43,21 @@ public class FieldEntry implements Entry {
54 43
55 @Override 44 @Override
56 public String getClassName() { 45 public String getClassName() {
57 return this.classEntry.getName(); 46 return this.ownerEntry.getName();
58 } 47 }
59 48
60 public Type getType() { 49 public TypeDescriptor getDesc() {
61 return this.type; 50 return this.desc;
62 } 51 }
63 52
64 @Override 53 @Override
65 public FieldEntry cloneToNewClass(ClassEntry classEntry) { 54 public FieldEntry updateOwnership(ClassEntry owner) {
66 return new FieldEntry(this, classEntry); 55 return new FieldEntry(owner, this.name, this.desc);
67 } 56 }
68 57
69 @Override 58 @Override
70 public int hashCode() { 59 public int hashCode() {
71 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); 60 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
72 } 61 }
73 62
74 @Override 63 @Override
@@ -77,11 +66,11 @@ public class FieldEntry implements Entry {
77 } 66 }
78 67
79 public boolean equals(FieldEntry other) { 68 public boolean equals(FieldEntry other) {
80 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); 69 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
81 } 70 }
82 71
83 @Override 72 @Override
84 public String toString() { 73 public String toString() {
85 return this.classEntry.getName() + "." + this.name + ":" + this.type; 74 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
86 } 75 }
87} 76}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index cd761b4..3c46a37 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -17,26 +17,19 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
17 17
18 private String obfName; 18 private String obfName;
19 private String deobfName; 19 private String deobfName;
20 private Type obfType; 20 private TypeDescriptor obfDesc;
21 private Mappings.EntryModifier modifier; 21 private Mappings.EntryModifier modifier;
22 22
23 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { 23 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
24 this.obfName = obfName; 24 this.obfName = obfName;
25 this.deobfName = NameValidator.validateFieldName(deobfName); 25 this.deobfName = NameValidator.validateFieldName(deobfName);
26 this.obfType = obfType; 26 this.obfDesc = obfDesc;
27 this.modifier = modifier; 27 this.modifier = modifier;
28 } 28 }
29 29
30 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) {
31 this.obfName = other.obfName;
32 this.deobfName = other.deobfName;
33 this.modifier = other.modifier;
34 this.obfType = new Type(other.obfType, obfClassNameReplacer);
35 }
36
37 @Override 30 @Override
38 public FieldEntry getObfEntry(ClassEntry classEntry) { 31 public FieldEntry getObfEntry(ClassEntry classEntry) {
39 return new FieldEntry(classEntry, this.obfName, this.obfType); 32 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
40 } 33 }
41 34
42 @Override 35 @Override
@@ -65,12 +58,12 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
65 this.deobfName = NameValidator.validateFieldName(val); 58 this.deobfName = NameValidator.validateFieldName(val);
66 } 59 }
67 60
68 public Type getObfType() { 61 public TypeDescriptor getObfDesc() {
69 return this.obfType; 62 return this.obfDesc;
70 } 63 }
71 64
72 public void setObfType(Type val) { 65 public void setObfDesc(TypeDescriptor val) {
73 this.obfType = val; 66 this.obfDesc = val;
74 } 67 }
75 68
76 public Mappings.EntryModifier getModifier() { 69 public Mappings.EntryModifier getModifier() {
@@ -83,21 +76,20 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
83 76
84 @Override 77 @Override
85 public int compareTo(FieldMapping other) { 78 public int compareTo(FieldMapping other) {
86 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 79 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
87 } 80 }
88 81
89 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 82 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
90 // rename obf classes in the type 83 // rename obf classes in the desc
91 Type newType = new Type(this.obfType, className -> 84 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
92 {
93 if (className.equals(oldObfClassName)) { 85 if (className.equals(oldObfClassName)) {
94 return newObfClassName; 86 return newObfClassName;
95 } 87 }
96 return null; 88 return className;
97 }); 89 });
98 90
99 if (!newType.equals(this.obfType)) { 91 if (!newDesc.equals(this.obfDesc)) {
100 this.obfType = newType; 92 this.obfDesc = newDesc;
101 return true; 93 return true;
102 } 94 }
103 return false; 95 return false;
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java
new file mode 100644
index 0000000..cc677c5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java
@@ -0,0 +1,75 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.utils.Utils;
5
6/**
7 * TypeDescriptor...
8 * Created by Thog
9 * 19/10/2016
10 */
11public class LocalVariableDefEntry extends LocalVariableEntry {
12
13 protected final MethodDefEntry ownerEntry;
14 protected final TypeDescriptor desc;
15
16 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
17 super(ownerEntry, index, name);
18 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
19
20 this.ownerEntry = ownerEntry;
21 this.desc = desc;
22 }
23
24 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name) {
25 super(ownerEntry, index, name);
26
27 this.ownerEntry = ownerEntry;
28
29 int namedIndex = getNamedIndex();
30 if (namedIndex < 0) {
31 this.desc = TypeDescriptor.of(ownerEntry.getOwnerClassEntry().getName());
32 } else {
33 this.desc = ownerEntry.getDesc().getArgumentDescs().get(namedIndex);
34 }
35 }
36
37 @Override
38 public MethodDefEntry getOwnerEntry() {
39 return this.ownerEntry;
40 }
41
42 public TypeDescriptor getDesc() {
43 return desc;
44 }
45
46 public int getNamedIndex() {
47 // If we're not static, "this" is bound to index 0
48 int indexOffset = ownerEntry.getAccess().isStatic() ? 0 : 1;
49 return index - indexOffset;
50 }
51
52 @Override
53 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
54 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, desc);
55 }
56
57 @Override
58 public int hashCode() {
59 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
60 }
61
62 @Override
63 public boolean equals(Object other) {
64 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
65 }
66
67 public boolean equals(LocalVariableDefEntry other) {
68 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
69 }
70
71 @Override
72 public String toString() {
73 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
index 2bb5e3f..dcfd0ff 100644
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
@@ -1,52 +1,31 @@
1package cuchaz.enigma.mapping; 1package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Preconditions;
3import cuchaz.enigma.utils.Utils; 4import cuchaz.enigma.utils.Utils;
4 5
5/** 6/**
6 * Desc... 7 * TypeDescriptor...
7 * Created by Thog 8 * Created by Thog
8 * 19/10/2016 9 * 19/10/2016
9 */ 10 */
10public class LocalVariableEntry implements Entry { 11public class LocalVariableEntry implements Entry {
11 12
12 protected final BehaviorEntry behaviorEntry; 13 protected final MethodEntry ownerEntry;
13 protected final String name; 14 protected final String name;
14 protected final Type type;
15 protected final int index; 15 protected final int index;
16 16
17 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { 17 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
18 if (behaviorEntry == null) { 18 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
19 throw new IllegalArgumentException("Behavior cannot be null!"); 19 Preconditions.checkNotNull(name, "Variable name cannot be null");
20 } 20 Preconditions.checkArgument(index >= 0, "Index must be positive");
21 if (index < 0) { 21
22 throw new IllegalArgumentException("Index must be non-negative!"); 22 this.ownerEntry = ownerEntry;
23 }
24 if (name == null) {
25 throw new IllegalArgumentException("Variable name cannot be null!");
26 }
27 if (type == null) {
28 throw new IllegalArgumentException("Variable type cannot be null!");
29 }
30
31 this.behaviorEntry = behaviorEntry;
32 this.name = name; 23 this.name = name;
33 this.type = type;
34 this.index = index; 24 this.index = index;
35 } 25 }
36 26
37 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { 27 public MethodEntry getOwnerEntry() {
38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); 28 return this.ownerEntry;
39 this.name = other.name;
40 this.type = other.type;
41 this.index = other.index;
42 }
43
44 public BehaviorEntry getBehaviorEntry() {
45 return this.behaviorEntry;
46 }
47
48 public Type getType() {
49 return type;
50 } 29 }
51 30
52 public int getIndex() { 31 public int getIndex() {
@@ -59,31 +38,31 @@ public class LocalVariableEntry implements Entry {
59 } 38 }
60 39
61 @Override 40 @Override
62 public ClassEntry getClassEntry() { 41 public ClassEntry getOwnerClassEntry() {
63 return this.behaviorEntry.getClassEntry(); 42 return this.ownerEntry.getOwnerClassEntry();
64 } 43 }
65 44
66 @Override 45 @Override
67 public String getClassName() { 46 public String getClassName() {
68 return this.behaviorEntry.getClassName(); 47 return this.ownerEntry.getClassName();
69 } 48 }
70 49
71 @Override 50 @Override
72 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { 51 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
73 return new LocalVariableEntry(this, classEntry); 52 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name);
74 } 53 }
75 54
76 public String getMethodName() { 55 public String getMethodName() {
77 return this.behaviorEntry.getName(); 56 return this.ownerEntry.getName();
78 } 57 }
79 58
80 public Signature getMethodSignature() { 59 public MethodDescriptor getMethodDesc() {
81 return this.behaviorEntry.getSignature(); 60 return this.ownerEntry.getDesc();
82 } 61 }
83 62
84 @Override 63 @Override
85 public int hashCode() { 64 public int hashCode() {
86 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); 65 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
87 } 66 }
88 67
89 @Override 68 @Override
@@ -92,11 +71,11 @@ public class LocalVariableEntry implements Entry {
92 } 71 }
93 72
94 public boolean equals(LocalVariableEntry other) { 73 public boolean equals(LocalVariableEntry other) {
95 return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; 74 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
96 } 75 }
97 76
98 @Override 77 @Override
99 public String toString() { 78 public String toString() {
100 return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")"; 79 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
101 } 80 }
102} 81}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
index 91ecd10..193c566 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
@@ -11,18 +11,18 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public class ArgumentMapping implements Comparable<ArgumentMapping> { 14public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
15 15
16 private int index; 16 private int index;
17 private String name; 17 private String name;
18 18
19 // NOTE: this argument order is important for the MethodReader/MethodWriter 19 // NOTE: this argument order is important for the MethodReader/MethodWriter
20 public ArgumentMapping(int index, String name) { 20 public LocalVariableMapping(int index, String name) {
21 this.index = index; 21 this.index = index;
22 this.name = NameValidator.validateArgumentName(name); 22 this.name = NameValidator.validateArgumentName(name);
23 } 23 }
24 24
25 public ArgumentMapping(ArgumentMapping other) { 25 public LocalVariableMapping(LocalVariableMapping other) {
26 this.index = other.index; 26 this.index = other.index;
27 this.name = other.name; 27 this.name = other.name;
28 } 28 }
@@ -39,12 +39,12 @@ public class ArgumentMapping implements Comparable<ArgumentMapping> {
39 this.name = NameValidator.validateArgumentName(val); 39 this.name = NameValidator.validateArgumentName(val);
40 } 40 }
41 41
42 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { 42 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
43 return new ArgumentEntry(behaviorEntry, index, name); 43 return new LocalVariableEntry(methodEntry, index, name);
44 } 44 }
45 45
46 @Override 46 @Override
47 public int compareTo(ArgumentMapping other) { 47 public int compareTo(LocalVariableMapping other) {
48 return Integer.compare(this.index, other.index); 48 return Integer.compare(this.index, other.index);
49 } 49 }
50} 50}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index cf78ca3..cc1ec9c 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -15,6 +15,7 @@ import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex; 17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.throwables.MappingConflict; 19import cuchaz.enigma.throwables.MappingConflict;
19 20
20import java.io.File; 21import java.io.File;
@@ -96,11 +97,11 @@ public class Mappings {
96 97
97 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 98 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
98 switch (direction) { 99 switch (direction) {
99 case Deobfuscating: 100 case DEOBFUSCATING:
100 101
101 return new Translator(direction, this.classesByObf, index); 102 return new DirectionalTranslator(direction, this.classesByObf, index);
102 103
103 case Obfuscating: 104 case OBFUSCATING:
104 105
105 // fill in the missing deobf class entries with obf entries 106 // fill in the missing deobf class entries with obf entries
106 Map<String, ClassMapping> classes = Maps.newHashMap(); 107 Map<String, ClassMapping> classes = Maps.newHashMap();
@@ -114,9 +115,9 @@ public class Mappings {
114 115
115 // translate the translation index 116 // translate the translation index
116 // NOTE: this isn't actually recursive 117 // NOTE: this isn't actually recursive
117 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 118 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
118 119
119 return new Translator(direction, classes, deobfIndex); 120 return new DirectionalTranslator(direction, classes, deobfIndex);
120 121
121 default: 122 default:
122 throw new Error("Invalid translation direction!"); 123 throw new Error("Invalid translation direction!");
@@ -151,9 +152,9 @@ public class Mappings {
151 152
152 // add classes from method signatures 153 // add classes from method signatures
153 for (MethodMapping methodMapping : classMapping.methods()) { 154 for (MethodMapping methodMapping : classMapping.methods()) {
154 for (Type type : methodMapping.getObfSignature().types()) { 155 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
155 if (type.hasClass()) { 156 if (desc.containsType()) {
156 classNames.add(type.getClassEntry().getClassName()); 157 classNames.add(desc.getOwnerEntry().getClassName());
157 } 158 }
158 } 159 }
159 } 160 }
@@ -165,9 +166,9 @@ public class Mappings {
165 return this.classesByDeobf.containsKey(deobfName); 166 return this.classesByDeobf.containsKey(deobfName);
166 } 167 }
167 168
168 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 169 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
169 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 170 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
170 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 171 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
171 } 172 }
172 173
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 174 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
@@ -180,14 +181,14 @@ public class Mappings {
180 return false; 181 return false;
181 } 182 }
182 183
183 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 184 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
184 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 185 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
185 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 186 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
186 } 187 }
187 188
188 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 189 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
189 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 190 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
190 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 191 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
191 } 192 }
192 193
193 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 194 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
@@ -239,5 +240,19 @@ public class Mappings {
239 public String getFormattedName() { 240 public String getFormattedName() {
240 return " ACC:" + super.toString(); 241 return " ACC:" + super.toString();
241 } 242 }
243
244 public AccessFlags transform(AccessFlags access) {
245 switch (this) {
246 case PUBLIC:
247 return access.setPublic();
248 case PROTECTED:
249 return access.setProtected();
250 case PRIVATE:
251 return access.setPrivate();
252 case UNCHANGED:
253 default:
254 return access;
255 }
256 }
242 } 257 }
243} 258}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index 172641b..4d5be2f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -23,7 +23,7 @@ public class MappingsChecker {
23 private Map<ClassEntry, ClassMapping> droppedClassMappings; 23 private Map<ClassEntry, ClassMapping> droppedClassMappings;
24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; 24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
25 private Map<FieldEntry, FieldMapping> droppedFieldMappings; 25 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
26 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; 26 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
27 27
28 public MappingsChecker(JarIndex index) { 28 public MappingsChecker(JarIndex index) {
29 this.index = index; 29 this.index = index;
@@ -45,7 +45,7 @@ public class MappingsChecker {
45 return this.droppedFieldMappings; 45 return this.droppedFieldMappings;
46 } 46 }
47 47
48 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 48 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
49 return this.droppedMethodMappings; 49 return this.droppedMethodMappings;
50 } 50 }
51 51
@@ -77,10 +77,10 @@ public class MappingsChecker {
77 77
78 // check methods 78 // check methods
79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
80 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 80 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
81 if (!this.index.containsObfBehavior(obfBehaviorEntry)) { 81 if (!this.index.containsObfMethod(obfMethodEntry)) {
82 classMapping.removeMethodMapping(methodMapping); 82 classMapping.removeMethodMapping(methodMapping);
83 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 83 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
84 } 84 }
85 } 85 }
86 86
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index a0d4313..d1d5634 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -123,8 +123,8 @@ public class MappingsEnigmaReader {
123 return mappings; 123 return mappings;
124 } 124 }
125 125
126 private ArgumentMapping readArgument(String[] parts) { 126 private LocalVariableMapping readArgument(String[] parts) {
127 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); 127 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
128 } 128 }
129 129
130 private ClassMapping readClass(String[] parts, boolean makeSimple) { 130 private ClassMapping readClass(String[] parts, boolean makeSimple) {
@@ -150,27 +150,27 @@ public class MappingsEnigmaReader {
150 if (parts.length == 4) { 150 if (parts.length == 4) {
151 boolean access = parts[3].startsWith("ACC:"); 151 boolean access = parts[3].startsWith("ACC:");
152 if (access) 152 if (access)
153 mapping = new FieldMapping(parts[1], new Type(parts[2]), null, 153 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
154 Mappings.EntryModifier.valueOf(parts[3].substring(4))); 154 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
155 else 155 else
156 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); 156 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
157 } else if (parts.length == 5) 157 } else if (parts.length == 5)
158 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); 158 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
159 return mapping; 159 return mapping;
160 } 160 }
161 161
162 private MethodMapping readMethod(String[] parts) { 162 private MethodMapping readMethod(String[] parts) {
163 MethodMapping mapping = null; 163 MethodMapping mapping = null;
164 if (parts.length == 3) 164 if (parts.length == 3)
165 mapping = new MethodMapping(parts[1], new Signature(parts[2])); 165 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
166 else if (parts.length == 4) { 166 else if (parts.length == 4) {
167 boolean access = parts[3].startsWith("ACC:"); 167 boolean access = parts[3].startsWith("ACC:");
168 if (access) 168 if (access)
169 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); 169 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
170 else 170 else
171 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); 171 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
172 } else if (parts.length == 5) 172 } else if (parts.length == 5)
173 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], 173 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
174 Mappings.EntryModifier.valueOf(parts[4].substring(4))); 174 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
175 return mapping; 175 return mapping;
176 } 176 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index ba1b258..1929977 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -127,29 +127,29 @@ public class MappingsEnigmaWriter {
127 127
128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { 128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
129 if (fieldMapping.getDeobfName() == null) 129 if (fieldMapping.getDeobfName() == null)
130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), 130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
132 else 132 else
133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), 133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(),
134 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 134 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
135 } 135 }
136 136
137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
138 if (methodMapping.getDeobfName() == null) { 138 if (methodMapping.getDeobfName() == null) {
139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), 139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
141 } else { 141 } else {
142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), 142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(),
143 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 143 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
144 } 144 }
145 145
146 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 146 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
147 write(out, argumentMapping, depth + 1); 147 write(out, localVariableMapping, depth + 1);
148 } 148 }
149 } 149 }
150 150
151 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { 151 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
152 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); 152 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
153 } 153 }
154 154
155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 7126d2b..e215a0f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -25,12 +25,14 @@ import java.util.zip.GZIPOutputStream;
25 25
26public class MappingsRenamer { 26public class MappingsRenamer {
27 27
28 private JarIndex index; 28 private final JarIndex index;
29 private final ReferencedEntryPool entryPool;
29 private Mappings mappings; 30 private Mappings mappings;
30 31
31 public MappingsRenamer(JarIndex index, Mappings mappings) { 32 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
32 this.index = index; 33 this.index = index;
33 this.mappings = mappings; 34 this.mappings = mappings;
35 this.entryPool = entryPool;
34 } 36 }
35 37
36 public void setMappings(Mappings mappings) { 38 public void setMappings(Mappings mappings) {
@@ -46,7 +48,7 @@ public class MappingsRenamer {
46 48
47 if (deobfName != null) { 49 if (deobfName != null) {
48 // make sure we don't rename to an existing obf or deobf class 50 // make sure we don't rename to an existing obf or deobf class
49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { 51 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
50 throw new IllegalNameException(deobfName, "There is already a class with that name"); 52 throw new IllegalNameException(deobfName, "There is already a class with that name");
51 } 53 }
52 } 54 }
@@ -87,13 +89,13 @@ public class MappingsRenamer {
87 89
88 public void setFieldName(FieldEntry obf, String deobfName) { 90 public void setFieldName(FieldEntry obf, String deobfName) {
89 deobfName = NameValidator.validateFieldName(deobfName); 91 deobfName = NameValidator.validateFieldName(deobfName);
90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 92 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
91 ClassEntry definedClass = null; 93 ClassEntry definedClass = null;
92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) 94 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry))
93 definedClass = obf.getClassEntry(); 95 definedClass = obf.getOwnerClassEntry();
94 else { 96 else {
95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { 97 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { 98 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
97 definedClass = ancestorEntry; 99 definedClass = ancestorEntry;
98 break; 100 break;
99 } 101 }
@@ -101,42 +103,44 @@ public class MappingsRenamer {
101 } 103 }
102 104
103 if (definedClass != null) { 105 if (definedClass != null) {
104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); 106 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
107 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
105 if (className == null) 108 if (className == null)
106 className = definedClass.getClassName(); 109 className = definedClass.getClassName();
107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); 110 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
108 } 111 }
109 112
110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 113 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); 114 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
112 } 115 }
113 116
114 public void removeFieldMapping(FieldEntry obf) { 117 public void removeFieldMapping(FieldEntry obf) {
115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 118 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); 119 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
117 } 120 }
118 121
119 public void markFieldAsDeobfuscated(FieldEntry obf) { 122 public void markFieldAsDeobfuscated(FieldEntry obf) {
120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 123 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); 124 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
122 } 125 }
123 126
124 private void validateMethodTreeName(MethodEntry entry, String deobfName) { 127 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); 128 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
126 129
127 // TODO: Verify if I don't break things 130 // TODO: Verify if I don't break things
128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 131 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
129 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) 132 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
130 || index.containsObfBehavior(targetEntry)) { 133 || index.containsObfMethod(targetEntry)) {
131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); 134 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
135 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
132 if (deobfClassName == null) { 136 if (deobfClassName == null) {
133 deobfClassName = entry.getClassName(); 137 deobfClassName = entry.getClassName();
134 } 138 }
135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 139 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
136 } 140 }
137 141
138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { 142 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName); 143 validateMethodTreeName(entry.updateOwnership(child), deobfName);
140 } 144 }
141 } 145 }
142 146
@@ -155,20 +159,21 @@ public class MappingsRenamer {
155 159
156 public void setMethodName(MethodEntry obf, String deobfName) { 160 public void setMethodName(MethodEntry obf, String deobfName) {
157 deobfName = NameValidator.validateMethodName(deobfName); 161 deobfName = NameValidator.validateMethodName(deobfName);
158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 162 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 163 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
160 164
161 // TODO: Verify if I don't break things 165 // TODO: Verify if I don't break things
162 if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) 166 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
163 || index.containsObfBehavior(targetEntry)) { 167 || index.containsObfMethod(targetEntry)) {
164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); 168 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
169 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
165 if (deobfClassName == null) { 170 if (deobfClassName == null) {
166 deobfClassName = obf.getClassName(); 171 deobfClassName = obf.getClassName();
167 } 172 }
168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 173 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
169 } 174 }
170 175
171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); 176 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
172 } 177 }
173 178
174 public void removeMethodTreeMapping(MethodEntry obf) { 179 public void removeMethodTreeMapping(MethodEntry obf) {
@@ -176,8 +181,8 @@ public class MappingsRenamer {
176 } 181 }
177 182
178 public void removeMethodMapping(MethodEntry obf) { 183 public void removeMethodMapping(MethodEntry obf) {
179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 184 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null); 185 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
181 } 186 }
182 187
183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) { 188 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
@@ -185,30 +190,25 @@ public class MappingsRenamer {
185 } 190 }
186 191
187 public void markMethodAsDeobfuscated(MethodEntry obf) { 192 public void markMethodAsDeobfuscated(MethodEntry obf) {
188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 193 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); 194 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
190 } 195 }
191 196
192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { 197 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { 198 MethodEntry obfMethod = obf.getOwnerEntry();
194 setArgumentName(obf, deobfName);
195 return;
196 }
197
198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry();
199 199
200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); 200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
201 for (MethodEntry entry : implementations) { 201 for (MethodEntry entry : implementations) {
202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 202 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
203 if (classMapping != null) { 203 if (classMapping != null) {
204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); 204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
206 // TODO: Verify if I don't break things 206 // TODO: Verify if I don't break things
207 if (mapping != null) { 207 if (mapping != null) {
208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 208 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
209 if (argumentMapping.getIndex() != obf.getIndex()) { 209 if (localVariableMapping.getIndex() != obf.getIndex()) {
210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 210 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
211 || argumentMapping.getName().equals(deobfName)) { 211 || localVariableMapping.getName().equals(deobfName)) {
212 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 212 throw new IllegalNameException(deobfName, "There is already an argument with that name");
213 } 213 }
214 } 214 }
@@ -218,45 +218,45 @@ public class MappingsRenamer {
218 } 218 }
219 219
220 for (MethodEntry entry : implementations) { 220 for (MethodEntry entry : implementations) {
221 setArgumentName(new ArgumentEntry(obf, entry), deobfName); 221 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName()), deobfName);
222 } 222 }
223 } 223 }
224 224
225 public void setArgumentName(ArgumentEntry obf, String deobfName) { 225 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
226 deobfName = NameValidator.validateArgumentName(deobfName); 226 deobfName = NameValidator.validateArgumentName(deobfName);
227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 227 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); 228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
230 // TODO: Verify if I don't break things 230 // TODO: Verify if I don't break things
231 if (mapping != null) { 231 if (mapping != null) {
232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 232 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
233 if (argumentMapping.getIndex() != obf.getIndex()) { 233 if (localVariableMapping.getIndex() != obf.getIndex()) {
234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 234 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
235 || argumentMapping.getName().equals(deobfName)) { 235 || localVariableMapping.getName().equals(deobfName)) {
236 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 236 throw new IllegalNameException(deobfName, "There is already an argument with that name");
237 } 237 }
238 } 238 }
239 } 239 }
240 } 240 }
241 241
242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); 242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
243 } 243 }
244 244
245 public void removeArgumentMapping(ArgumentEntry obf) { 245 public void removeLocalVariableMapping(LocalVariableEntry obf) {
246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 246 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); 247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
248 } 248 }
249 249
250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) { 250 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 251 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
253 } 253 }
254 254
255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { 255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
256 classMapping.removeFieldMapping(fieldMapping); 256 classMapping.removeFieldMapping(fieldMapping);
257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { 258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { 259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
260 targetClassMapping.addFieldMapping(fieldMapping); 260 targetClassMapping.addFieldMapping(fieldMapping);
261 return true; 261 return true;
262 } else { 262 } else {
@@ -269,12 +269,12 @@ public class MappingsRenamer {
269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { 269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
270 classMapping.removeMethodMapping(methodMapping); 270 classMapping.removeMethodMapping(methodMapping);
271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { 272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { 273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
274 targetClassMapping.addMethodMapping(methodMapping); 274 targetClassMapping.addMethodMapping(methodMapping);
275 return true; 275 return true;
276 } else { 276 } else {
277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); 277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
278 } 278 }
279 } 279 }
280 return false; 280 return false;
@@ -326,12 +326,35 @@ public class MappingsRenamer {
326 } 326 }
327 327
328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { 328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); 330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
331 }
332
333 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
336 }
337
338 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
339 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
340 return classMapping.getModifier();
331 } 341 }
332 342
333 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { 343 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 344 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); 345 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
346 if (fieldMapping == null) {
347 return Mappings.EntryModifier.UNCHANGED;
348 }
349 return fieldMapping.getModifier();
350 }
351
352 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
353 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
354 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
355 if (methodMapping == null) {
356 return Mappings.EntryModifier.UNCHANGED;
357 }
358 return methodMapping.getModifier();
336 } 359 }
337} 360}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index b0eb826..95daa73 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -19,7 +19,7 @@ public class MappingsSRGWriter {
19 } 19 }
20 file.createNewFile(); 20 file.createNewFile();
21 21
22 TranslationIndex index = new TranslationIndex(); 22 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
23 23
24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); 24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
25 List<String> fieldMappings = new ArrayList<>(); 25 List<String> fieldMappings = new ArrayList<>();
@@ -43,7 +43,7 @@ public class MappingsSRGWriter {
43 } 43 }
44 44
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
47 } 47 }
48 } 48 }
49 49
@@ -52,7 +52,7 @@ public class MappingsSRGWriter {
52 } 52 }
53 53
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 54 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
56 } 56 }
57 } 57 }
58 for (String fd : fieldMappings) { 58 for (String fd : fieldMappings) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
index dfe9e88..e635fa1 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
@@ -20,11 +20,11 @@ public class MappingsTinyReader {
20 } 20 }
21 21
22 public FieldMapping readField(String[] parts) { 22 public FieldMapping readField(String[] parts) {
23 return new FieldMapping(parts[3], new Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); 23 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
24 } 24 }
25 25
26 public MethodMapping readMethod(String[] parts) { 26 public MethodMapping readMethod(String[] parts) {
27 return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); 27 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
28 } 28 }
29 29
30 public Mappings read(File file) throws IOException, MappingParseException { 30 public Mappings read(File file) throws IOException, MappingParseException {
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java
new file mode 100644
index 0000000..d6a160d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java
@@ -0,0 +1,35 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class MethodDefEntry extends MethodEntry {
18
19 private final AccessFlags access;
20
21 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, AccessFlags access) {
22 super(classEntry, name, descriptor);
23 Preconditions.checkNotNull(access, "Method access cannot be null");
24 this.access = access;
25 }
26
27 public AccessFlags getAccess() {
28 return access;
29 }
30
31 @Override
32 public MethodDefEntry updateOwnership(ClassEntry classEntry) {
33 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, access);
34 }
35}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
new file mode 100644
index 0000000..210ada0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
@@ -0,0 +1,113 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.utils.Utils;
16
17import java.util.ArrayList;
18import java.util.List;
19import java.util.function.Function;
20
21public class MethodDescriptor {
22
23 private List<TypeDescriptor> argumentDescs;
24 private TypeDescriptor returnDesc;
25
26 public MethodDescriptor(String desc) {
27 try {
28 this.argumentDescs = Lists.newArrayList();
29 int i = 0;
30 while (i < desc.length()) {
31 char c = desc.charAt(i);
32 if (c == '(') {
33 assert (this.argumentDescs.isEmpty());
34 assert (this.returnDesc == null);
35 i++;
36 } else if (c == ')') {
37 i++;
38 break;
39 } else {
40 String type = TypeDescriptor.parseFirst(desc.substring(i));
41 this.argumentDescs.add(new TypeDescriptor(type));
42 i += type.length();
43 }
44 }
45 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
46 } catch (Exception ex) {
47 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
48 }
49 }
50
51 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
52 this.argumentDescs = argumentDescs;
53 this.returnDesc = returnDesc;
54 }
55
56 public List<TypeDescriptor> getArgumentDescs() {
57 return this.argumentDescs;
58 }
59
60 public TypeDescriptor getReturnDesc() {
61 return this.returnDesc;
62 }
63
64 @Override
65 public String toString() {
66 StringBuilder buf = new StringBuilder();
67 buf.append("(");
68 for (TypeDescriptor desc : this.argumentDescs) {
69 buf.append(desc);
70 }
71 buf.append(")");
72 buf.append(this.returnDesc);
73 return buf.toString();
74 }
75
76 public Iterable<TypeDescriptor> types() {
77 List<TypeDescriptor> descs = Lists.newArrayList();
78 descs.addAll(this.argumentDescs);
79 descs.add(this.returnDesc);
80 return descs;
81 }
82
83 @Override
84 public boolean equals(Object other) {
85 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
86 }
87
88 public boolean equals(MethodDescriptor other) {
89 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
90 }
91
92 @Override
93 public int hashCode() {
94 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
95 }
96
97 public boolean hasClass(ClassEntry classEntry) {
98 for (TypeDescriptor desc : types()) {
99 if (desc.containsType() && desc.getOwnerEntry().equals(classEntry)) {
100 return true;
101 }
102 }
103 return false;
104 }
105
106 public MethodDescriptor remap(Function<String, String> remapper) {
107 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
108 for (TypeDescriptor desc : this.argumentDescs) {
109 argumentDescs.add(desc.remap(remapper));
110 }
111 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
index 9c3058c..f8a5ff1 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
@@ -11,41 +11,27 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
15 16
16public class MethodEntry implements BehaviorEntry { 17public class MethodEntry implements Entry {
17 18
18 private ClassEntry classEntry; 19 protected final ClassEntry classEntry;
19 private String name; 20 protected final String name;
20 private Signature signature; 21 protected final MethodDescriptor descriptor;
21 22
22 public MethodEntry(ClassEntry classEntry, String name, Signature signature) { 23 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
23 if (classEntry == null) { 24 Preconditions.checkNotNull(classEntry, "Class cannot be null");
24 throw new IllegalArgumentException("Class cannot be null!"); 25 Preconditions.checkNotNull(name, "Method name cannot be null");
25 } 26 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
26 if (name == null) {
27 throw new IllegalArgumentException("Method name cannot be null!");
28 }
29 if (signature == null) {
30 throw new IllegalArgumentException("Method signature cannot be null!");
31 }
32 if (name.startsWith("<")) {
33 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!");
34 }
35 27
36 this.classEntry = classEntry; 28 this.classEntry = classEntry;
37 this.name = name; 29 this.name = name;
38 this.signature = signature; 30 this.descriptor = descriptor;
39 }
40
41 public MethodEntry(MethodEntry other, String newClassName) {
42 this.classEntry = new ClassEntry(newClassName);
43 this.name = other.name;
44 this.signature = other.signature;
45 } 31 }
46 32
47 @Override 33 @Override
48 public ClassEntry getClassEntry() { 34 public ClassEntry getOwnerClassEntry() {
49 return this.classEntry; 35 return this.classEntry;
50 } 36 }
51 37
@@ -54,9 +40,12 @@ public class MethodEntry implements BehaviorEntry {
54 return this.name; 40 return this.name;
55 } 41 }
56 42
57 @Override 43 public MethodDescriptor getDesc() {
58 public Signature getSignature() { 44 return this.descriptor;
59 return this.signature; 45 }
46
47 public boolean isConstructor() {
48 return name.equals("<init>") || name.equals("<clinit>");
60 } 49 }
61 50
62 @Override 51 @Override
@@ -65,13 +54,13 @@ public class MethodEntry implements BehaviorEntry {
65 } 54 }
66 55
67 @Override 56 @Override
68 public MethodEntry cloneToNewClass(ClassEntry classEntry) { 57 public MethodEntry updateOwnership(ClassEntry classEntry) {
69 return new MethodEntry(this, classEntry.getName()); 58 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
70 } 59 }
71 60
72 @Override 61 @Override
73 public int hashCode() { 62 public int hashCode() {
74 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); 63 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
75 } 64 }
76 65
77 @Override 66 @Override
@@ -80,11 +69,11 @@ public class MethodEntry implements BehaviorEntry {
80 } 69 }
81 70
82 public boolean equals(MethodEntry other) { 71 public boolean equals(MethodEntry other) {
83 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); 72 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
84 } 73 }
85 74
86 @Override 75 @Override
87 public String toString() { 76 public String toString() {
88 return this.classEntry.getName() + "." + this.name + this.signature; 77 return this.classEntry.getName() + "." + this.name + this.descriptor;
89 } 78 }
90} 79}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index 1524ce6..2f7fe53 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -11,50 +11,47 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
15import cuchaz.enigma.throwables.IllegalNameException; 16import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.throwables.MappingConflict; 17import cuchaz.enigma.throwables.MappingConflict;
17 18
18import java.util.Map; 19import java.util.Map;
19 20
20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 21public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
21 22
22 private String obfName; 23 private String obfName;
23 private String deobfName; 24 private String deobfName;
24 private Signature obfSignature; 25 private MethodDescriptor obfDescriptor;
25 private Map<Integer, ArgumentMapping> arguments; 26 private Map<Integer, LocalVariableMapping> localVariables;
26 private Mappings.EntryModifier modifier; 27 private Mappings.EntryModifier modifier;
27 28
28 public MethodMapping(String obfName, Signature obfSignature) { 29 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
29 this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); 30 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
30 } 31 }
31 32
32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 33 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); 34 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
34 } 35 }
35 36
36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { 37 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
37 if (obfName == null) { 38 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
38 throw new IllegalArgumentException("obf name cannot be null!"); 39 Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null");
39 }
40 if (obfSignature == null) {
41 throw new IllegalArgumentException("obf signature cannot be null!");
42 }
43 this.obfName = obfName; 40 this.obfName = obfName;
44 this.deobfName = NameValidator.validateMethodName(deobfName); 41 this.deobfName = NameValidator.validateMethodName(deobfName);
45 this.obfSignature = obfSignature; 42 this.obfDescriptor = obfDescriptor;
46 this.arguments = Maps.newTreeMap(); 43 this.localVariables = Maps.newTreeMap();
47 this.modifier = modifier; 44 this.modifier = modifier;
48 } 45 }
49 46
50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 47 public MethodMapping(MethodMapping other, Translator translator) {
51 this.obfName = other.obfName; 48 this.obfName = other.obfName;
52 this.deobfName = other.deobfName; 49 this.deobfName = other.deobfName;
53 this.modifier = other.modifier; 50 this.modifier = other.modifier;
54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 51 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
55 this.arguments = Maps.newTreeMap(); 52 this.localVariables = Maps.newTreeMap();
56 for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) { 53 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); 54 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
58 } 55 }
59 } 56 }
60 57
@@ -84,56 +81,56 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
84 this.deobfName = NameValidator.validateMethodName(val); 81 this.deobfName = NameValidator.validateMethodName(val);
85 } 82 }
86 83
87 public Signature getObfSignature() { 84 public MethodDescriptor getObfDesc() {
88 return this.obfSignature; 85 return this.obfDescriptor;
89 } 86 }
90 87
91 public void setObfSignature(Signature val) { 88 public void setObfDescriptor(MethodDescriptor val) {
92 this.obfSignature = val; 89 this.obfDescriptor = val;
93 } 90 }
94 91
95 public Iterable<ArgumentMapping> arguments() { 92 public Iterable<LocalVariableMapping> arguments() {
96 return this.arguments.values(); 93 return this.localVariables.values();
97 } 94 }
98 95
99 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { 96 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
100 if (this.arguments.containsKey(argumentMapping.getIndex())) { 97 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
101 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); 98 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
102 } 99 }
103 this.arguments.put(argumentMapping.getIndex(), argumentMapping); 100 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
104 } 101 }
105 102
106 public String getObfArgumentName(int index) { 103 public String getObfLocalVariableName(int index) {
107 ArgumentMapping argumentMapping = this.arguments.get(index); 104 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
108 if (argumentMapping != null) { 105 if (localVariableMapping != null) {
109 return argumentMapping.getName(); 106 return localVariableMapping.getName();
110 } 107 }
111 108
112 return null; 109 return null;
113 } 110 }
114 111
115 public String getDeobfArgumentName(int index) { 112 public String getDeobfLocalVariableName(int index) {
116 ArgumentMapping argumentMapping = this.arguments.get(index); 113 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
117 if (argumentMapping != null) { 114 if (localVariableMapping != null) {
118 return argumentMapping.getName(); 115 return localVariableMapping.getName();
119 } 116 }
120 117
121 return null; 118 return null;
122 } 119 }
123 120
124 public void setArgumentName(int index, String name) { 121 public void setLocalVariableName(int index, String name) {
125 ArgumentMapping argumentMapping = this.arguments.get(index); 122 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
126 if (argumentMapping == null) { 123 if (localVariableMapping == null) {
127 argumentMapping = new ArgumentMapping(index, name); 124 localVariableMapping = new LocalVariableMapping(index, name);
128 boolean wasAdded = this.arguments.put(index, argumentMapping) == null; 125 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
129 assert (wasAdded); 126 assert (wasAdded);
130 } else { 127 } else {
131 argumentMapping.setName(name); 128 localVariableMapping.setName(name);
132 } 129 }
133 } 130 }
134 131
135 public void removeArgumentName(int index) { 132 public void removeLocalVariableName(int index) {
136 boolean wasRemoved = this.arguments.remove(index) != null; 133 boolean wasRemoved = this.localVariables.remove(index) != null;
137 assert (wasRemoved); 134 assert (wasRemoved);
138 } 135 }
139 136
@@ -146,14 +143,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
146 buf.append(this.deobfName); 143 buf.append(this.deobfName);
147 buf.append("\n"); 144 buf.append("\n");
148 buf.append("\t"); 145 buf.append("\t");
149 buf.append(this.obfSignature); 146 buf.append(this.obfDescriptor);
150 buf.append("\n"); 147 buf.append("\n");
151 buf.append("\tArguments:\n"); 148 buf.append("\tLocal Variables:\n");
152 for (ArgumentMapping argumentMapping : this.arguments.values()) { 149 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
153 buf.append("\t\t"); 150 buf.append("\t\t");
154 buf.append(argumentMapping.getIndex()); 151 buf.append(localVariableMapping.getIndex());
155 buf.append(" -> "); 152 buf.append(" -> ");
156 buf.append(argumentMapping.getName()); 153 buf.append(localVariableMapping.getName());
157 buf.append("\n"); 154 buf.append("\n");
158 } 155 }
159 return buf.toString(); 156 return buf.toString();
@@ -161,12 +158,12 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
161 158
162 @Override 159 @Override
163 public int compareTo(MethodMapping other) { 160 public int compareTo(MethodMapping other) {
164 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); 161 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
165 } 162 }
166 163
167 public boolean containsArgument(String name) { 164 public boolean containsLocalVariable(String name) {
168 for (ArgumentMapping argumentMapping : this.arguments.values()) { 165 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
169 if (argumentMapping.getName().equals(name)) { 166 if (localVariableMapping.getName().equals(name)) {
170 return true; 167 return true;
171 } 168 }
172 } 169 }
@@ -175,32 +172,23 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
175 172
176 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 173 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
177 // rename obf classes in the signature 174 // rename obf classes in the signature
178 Signature newSignature = new Signature(this.obfSignature, className -> 175 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
179 {
180 if (className.equals(oldObfClassName)) { 176 if (className.equals(oldObfClassName)) {
181 return newObfClassName; 177 return newObfClassName;
182 } 178 }
183 return null; 179 return className;
184 }); 180 });
185 181
186 if (!newSignature.equals(this.obfSignature)) { 182 if (!newDescriptor.equals(this.obfDescriptor)) {
187 this.obfSignature = newSignature; 183 this.obfDescriptor = newDescriptor;
188 return true; 184 return true;
189 } 185 }
190 return false; 186 return false;
191 } 187 }
192 188
193 public boolean isConstructor() {
194 return this.obfName.startsWith("<");
195 }
196
197 @Override 189 @Override
198 public BehaviorEntry getObfEntry(ClassEntry classEntry) { 190 public MethodEntry getObfEntry(ClassEntry classEntry) {
199 if (isConstructor()) { 191 return new MethodEntry(classEntry, this.obfName, this.obfDescriptor);
200 return new ConstructorEntry(classEntry, this.obfSignature);
201 } else {
202 return new MethodEntry(classEntry, this.obfName, this.obfSignature);
203 }
204 } 192 }
205 193
206 public Mappings.EntryModifier getModifier() { 194 public Mappings.EntryModifier getModifier() {
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index aa3dc4d..f178093 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.throwables.IllegalNameException; 14import cuchaz.enigma.throwables.IllegalNameException;
15import javassist.bytecode.Descriptor;
16 15
17import java.util.Arrays; 16import java.util.Arrays;
18import java.util.List; 17import java.util.List;
@@ -23,11 +22,11 @@ public class NameValidator {
23 private static final Pattern IdentifierPattern; 22 private static final Pattern IdentifierPattern;
24 private static final Pattern ClassPattern; 23 private static final Pattern ClassPattern;
25 private static final List<String> ReservedWords = Arrays.asList( 24 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 25 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 26 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 27 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", 28 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while" 29 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
31 ); 30 );
32 31
33 static { 32 static {
@@ -43,10 +42,10 @@ public class NameValidator {
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { 42 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 43 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 } 44 }
46 if (packageRequired && new ClassEntry(name).getPackageName() == null) { 45 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package"); 46 throw new IllegalNameException(name, "Class must be in a package");
48 } 47 }
49 return Descriptor.toJvmName(name); 48 return name;
50 } 49 }
51 50
52 public static String validateFieldName(String name) { 51 public static String validateFieldName(String name) {
diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
index 33d930d..9300656 100644
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
@@ -12,12 +12,18 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.strobel.assembler.metadata.*; 14import com.strobel.assembler.metadata.*;
15import cuchaz.enigma.bytecode.AccessFlags;
15 16
16import java.util.List; 17import java.util.List;
17 18
18public class ProcyonEntryFactory { 19public class ProcyonEntryFactory {
20 private final ReferencedEntryPool entryPool;
19 21
20 private static String getErasedSignature(MemberReference def) { 22 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
23 this.entryPool = entryPool;
24 }
25
26 private String getErasedSignature(MemberReference def) {
21 if (!(def instanceof MethodReference)) 27 if (!(def instanceof MethodReference))
22 return def.getErasedSignature(); 28 return def.getErasedSignature();
23 MethodReference methodReference = (MethodReference) def; 29 MethodReference methodReference = (MethodReference) def;
@@ -41,27 +47,23 @@ public class ProcyonEntryFactory {
41 return builder.toString(); 47 return builder.toString();
42 } 48 }
43 49
44 public static FieldEntry getFieldEntry(MemberReference def) { 50 public FieldEntry getFieldEntry(MemberReference def) {
45 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); 51 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
52 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature());
46 } 53 }
47 54
48 public static MethodEntry getMethodEntry(MemberReference def) { 55 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
49 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); 56 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
57 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers()));
50 } 58 }
51 59
52 public static ConstructorEntry getConstructorEntry(MethodReference def) { 60 public MethodEntry getMethodEntry(MemberReference def) {
53 if (def.isTypeInitializer()) { 61 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
54 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); 62 return entryPool.getMethod(classEntry, def.getName(), getErasedSignature(def));
55 } else {
56 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
57 }
58 } 63 }
59 64
60 public static BehaviorEntry getBehaviorEntry(MethodReference def) { 65 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
61 if (def.isConstructor() || def.isTypeInitializer()) { 66 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
62 return getConstructorEntry(def); 67 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers()));
63 } else {
64 return getMethodEntry(def);
65 }
66 } 68 }
67} 69}
diff --git a/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java
new file mode 100644
index 0000000..2abc76c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java
@@ -0,0 +1,50 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import java.util.HashMap;
15import java.util.Map;
16
17public class ReferencedEntryPool {
18 private final Map<String, ClassEntry> classEntries = new HashMap<>();
19 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
20 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
21
22 public ClassEntry getClass(String name) {
23 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(name));
24 }
25
26 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
27 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
28 }
29
30 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
31 String key = name + desc.toString();
32 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
33 }
34
35 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
36 return getField(ownerEntry, name, new TypeDescriptor(desc));
37 }
38
39 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
40 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
41 }
42
43 private Map<String, MethodEntry> getClassMethods(String name) {
44 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
45 }
46
47 private Map<String, FieldEntry> getClassFields(String name) {
48 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
49 }
50}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
deleted file mode 100644
index 78130d6..0000000
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ /dev/null
@@ -1,106 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.utils.Utils;
16
17import java.util.List;
18
19public class Signature {
20
21 private List<Type> argumentTypes;
22 private Type returnType;
23
24 public Signature(String signature) {
25 try {
26 this.argumentTypes = Lists.newArrayList();
27 int i = 0;
28 while (i < signature.length()) {
29 char c = signature.charAt(i);
30 if (c == '(') {
31 assert (this.argumentTypes.isEmpty());
32 assert (this.returnType == null);
33 i++;
34 } else if (c == ')') {
35 i++;
36 break;
37 } else {
38 String type = Type.parseFirst(signature.substring(i));
39 this.argumentTypes.add(new Type(type));
40 i += type.length();
41 }
42 }
43 this.returnType = new Type(Type.parseFirst(signature.substring(i)));
44 } catch (Exception ex) {
45 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex);
46 }
47 }
48
49 public Signature(Signature other, ClassNameReplacer replacer) {
50 this.argumentTypes = Lists.newArrayList(other.argumentTypes);
51 for (int i = 0; i < this.argumentTypes.size(); i++) {
52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
53 }
54 this.returnType = new Type(other.returnType, replacer);
55 }
56
57 public List<Type> getArgumentTypes() {
58 return this.argumentTypes;
59 }
60
61 public Type getReturnType() {
62 return this.returnType;
63 }
64
65 @Override
66 public String toString() {
67 StringBuilder buf = new StringBuilder();
68 buf.append("(");
69 for (Type type : this.argumentTypes) {
70 buf.append(type);
71 }
72 buf.append(")");
73 buf.append(this.returnType);
74 return buf.toString();
75 }
76
77 public Iterable<Type> types() {
78 List<Type> types = Lists.newArrayList();
79 types.addAll(this.argumentTypes);
80 types.add(this.returnType);
81 return types;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 return other instanceof Signature && equals((Signature) other);
87 }
88
89 public boolean equals(Signature other) {
90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType);
91 }
92
93 @Override
94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode());
96 }
97
98 public boolean hasClass(ClassEntry classEntry) {
99 for (Type type : types()) {
100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) {
101 return true;
102 }
103 }
104 return false;
105 }
106}
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
index 17e3187..4bbde54 100644
--- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
+++ b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
@@ -13,15 +13,21 @@ package cuchaz.enigma.mapping;
13 13
14public enum TranslationDirection { 14public enum TranslationDirection {
15 15
16 Deobfuscating { 16 DEOBFUSCATING {
17 @Override 17 @Override
18 public <T> T choose(T deobfChoice, T obfChoice) { 18 public <T> T choose(T deobfChoice, T obfChoice) {
19 if (deobfChoice == null) {
20 return obfChoice;
21 }
19 return deobfChoice; 22 return deobfChoice;
20 } 23 }
21 }, 24 },
22 Obfuscating { 25 OBFUSCATING {
23 @Override 26 @Override
24 public <T> T choose(T deobfChoice, T obfChoice) { 27 public <T> T choose(T deobfChoice, T obfChoice) {
28 if (obfChoice == null) {
29 return deobfChoice;
30 }
25 return obfChoice; 31 return obfChoice;
26 } 32 }
27 }; 33 };
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
index 8d464fc..59bdf1a 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -11,332 +11,50 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14public interface Translator {
15import com.google.common.collect.Maps; 15 ClassEntry getTranslatedClass(ClassEntry entry);
16import cuchaz.enigma.analysis.TranslationIndex;
17 16
18import java.util.List; 17 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
19import java.util.Map;
20 18
21public class Translator { 19 FieldEntry getTranslatedField(FieldEntry entry);
22 20
23 private TranslationDirection direction; 21 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
24 private Map<String, ClassMapping> classes;
25 private TranslationIndex index;
26 22
27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 23 MethodEntry getTranslatedMethod(MethodEntry entry);
28 24
29 public Translator() { 25 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex();
33 }
34 26
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 27 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40 28
41 public TranslationDirection getDirection() { 29 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
42 return direction;
43 }
44 30
45 public TranslationIndex getTranslationIndex() { 31 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
46 return index;
47 }
48 32
49 @SuppressWarnings("unchecked") 33 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
50 public <T extends Entry> T translateEntry(T entry) {
51 if (entry instanceof ClassEntry) {
52 return (T) translateEntry((ClassEntry) entry);
53 } else if (entry instanceof FieldEntry) {
54 return (T) translateEntry((FieldEntry) entry);
55 } else if (entry instanceof MethodEntry) {
56 return (T) translateEntry((MethodEntry) entry);
57 } else if (entry instanceof ConstructorEntry) {
58 return (T) translateEntry((ConstructorEntry) entry);
59 } else if (entry instanceof ArgumentEntry) {
60 return (T) translateEntry((ArgumentEntry) entry);
61 } else if (entry instanceof LocalVariableEntry) {
62 return (T) translateEntry((LocalVariableEntry) entry);
63 } else {
64 throw new Error("Unknown entry type: " + entry.getClass().getName());
65 }
66 }
67 34
68 public <T extends Entry> String translate(T entry) { 35 @SuppressWarnings("unchecked")
69 if (entry instanceof ClassEntry) { 36 default <T extends Entry> T getTranslatedEntry(T entry) {
70 return translate((ClassEntry) entry); 37 if (entry instanceof ClassDefEntry) {
38 return (T) getTranslatedClassDef((ClassDefEntry) entry);
39 } else if (entry instanceof ClassEntry) {
40 return (T) getTranslatedClass((ClassEntry) entry);
41 } else if (entry instanceof FieldDefEntry) {
42 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
43 } else if (entry instanceof MethodDefEntry) {
44 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
71 } else if (entry instanceof FieldEntry) { 45 } else if (entry instanceof FieldEntry) {
72 return translate((FieldEntry) entry); 46 return (T) getTranslatedField((FieldEntry) entry);
73 } else if (entry instanceof MethodEntry) { 47 } else if (entry instanceof MethodEntry) {
74 return translate((MethodEntry) entry); 48 return (T) getTranslatedMethod((MethodEntry) entry);
75 } else if (entry instanceof ConstructorEntry) { 49 } else if (entry instanceof LocalVariableDefEntry) {
76 return translate(entry); 50 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
77 } else if (entry instanceof ArgumentEntry) {
78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) { 51 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry); 52 return (T) getTranslatedVariable((LocalVariableEntry) entry);
81 } else { 53 } else if (entry instanceof TypeDescriptor) {
82 throw new Error("Unknown entry type: " + entry.getClass().getName()); 54 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
83 } 55 } else if (entry instanceof MethodDescriptor) {
84 } 56 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
85
86 public String translate(LocalVariableEntry in) {
87 LocalVariableEntry translated = translateEntry(in);
88 if (translated.equals(in)) {
89 return null;
90 }
91 return translated.getName();
92 }
93
94 public LocalVariableEntry translateEntry(LocalVariableEntry in) {
95 // TODO: Implement it
96 return in;
97 }
98
99 public String translate(ClassEntry in) {
100 ClassEntry translated = translateEntry(in);
101 if (translated.equals(in)) {
102 return null;
103 }
104 return translated.getName();
105 }
106
107 public String translateClass(String className) {
108 return translate(new ClassEntry(className));
109 }
110
111 public ClassEntry translateEntry(ClassEntry in) {
112
113 if (in.isInnerClass()) {
114
115 // translate as much of the class chain as we can
116 List<ClassMapping> mappingsChain = getClassMappingChain(in);
117 String[] obfClassNames = in.getName().split("\\$");
118 StringBuilder buf = new StringBuilder();
119 for (int i = 0; i < obfClassNames.length; i++) {
120 boolean isFirstClass = buf.length() == 0;
121 String className = null;
122 ClassMapping classMapping = mappingsChain.get(i);
123 if (classMapping != null) {
124 className = this.direction.choose(
125 classMapping.getDeobfName(),
126 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
127 );
128 }
129 if (className == null) {
130 className = obfClassNames[i];
131 }
132 if (!isFirstClass) {
133 buf.append("$");
134 }
135 buf.append(className);
136 }
137 return new ClassEntry(buf.toString());
138
139 } else {
140
141 // normal classes are easy
142 ClassMapping classMapping = this.classes.get(in.getName());
143 if (classMapping == null) {
144 return in;
145 }
146 return this.direction.choose(
147 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in,
148 new ClassEntry(classMapping.getObfFullName())
149 );
150 }
151 }
152
153 public String translate(FieldEntry in) {
154
155 // resolve the class entry
156 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in);
157 if (resolvedClassEntry != null) {
158
159 // look for the class
160 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
161 if (classMapping != null) {
162
163 // look for the field
164 String translatedName = this.direction.choose(
165 classMapping.getDeobfFieldName(in.getName(), in.getType()),
166 classMapping.getObfFieldName(in.getName(), translateType(in.getType()))
167 );
168 if (translatedName != null) {
169 return translatedName;
170 }
171 }
172 }
173 return null;
174 }
175
176 public FieldEntry translateEntry(FieldEntry in) {
177 String name = translate(in);
178 if (name == null) {
179 name = in.getName();
180 }
181 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType()));
182 }
183
184 public String translate(MethodEntry in) {
185 // resolve the class entry
186 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true);
187 if (resolvedClassEntry != null) {
188
189 // look for class
190 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
191 if (classMapping != null) {
192
193 // look for the method
194 MethodMapping methodMapping = this.direction.choose(
195 classMapping.getMethodByObf(in.getName(), in.getSignature()),
196 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
197 );
198 if (methodMapping != null) {
199 return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
200 }
201 }
202 }
203 return null;
204 }
205
206 public MethodEntry translateEntry(MethodEntry in) {
207 String name = translate(in);
208 if (name == null) {
209 name = in.getName();
210 }
211 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature()));
212 }
213
214 public ConstructorEntry translateEntry(ConstructorEntry in) {
215 if (in.isStatic()) {
216 return new ConstructorEntry(translateEntry(in.getClassEntry()));
217 } else {
218 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature()));
219 }
220 }
221
222 public BehaviorEntry translateEntry(BehaviorEntry in) {
223 if (in instanceof MethodEntry) {
224 return translateEntry((MethodEntry) in);
225 } else if (in instanceof ConstructorEntry) {
226 return translateEntry((ConstructorEntry) in);
227 }
228 throw new Error("Wrong entry type!");
229 }
230
231 // TODO: support not identical behavior (specific to constructor)
232 public String translate(ArgumentEntry in) {
233 String classTranslate = translateArgument(in);
234
235 // Not found in this class
236 if (classTranslate == null) {
237 List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry());
238
239 // Check in mother class for the arg
240 for (ClassEntry entry : ancestry) {
241 ArgumentEntry motherArg = in.cloneToNewClass(entry);
242 if (this.index.entryExists(motherArg)) {
243 String result = translateArgument(motherArg);
244 if (result != null)
245 return result;
246 }
247 }
248 }
249 return classTranslate;
250 }
251
252 public String translateArgument(ArgumentEntry in) {
253 // look for identical behavior in superclasses
254 ClassEntry entry = in.getClassEntry();
255
256 if (entry != null) {
257 // look for the class
258 ClassMapping classMapping = findClassMapping(entry);
259 if (classMapping != null) {
260
261 // look for the method
262 MethodMapping methodMapping = this.direction.choose(
263 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()),
264 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature()))
265 );
266 if (methodMapping != null) {
267 return this.direction.choose(
268 methodMapping.getDeobfArgumentName(in.getIndex()),
269 methodMapping.getObfArgumentName(in.getIndex())
270 );
271 }
272 }
273 }
274 return null;
275 }
276
277 public ArgumentEntry translateEntry(ArgumentEntry in) {
278 String name = translate(in);
279 if (name == null) {
280 name = in.getName();
281 }
282 return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name);
283 }
284
285 public Type translateType(Type type) {
286 return new Type(type, this.classNameReplacer);
287 }
288
289 public Signature translateSignature(Signature signature) {
290 return new Signature(signature, this.classNameReplacer);
291 }
292
293 private ClassMapping findClassMapping(ClassEntry in) {
294 List<ClassMapping> mappingChain = getClassMappingChain(in);
295 return mappingChain.get(mappingChain.size() - 1);
296 }
297
298 private List<ClassMapping> getClassMappingChain(ClassEntry in) {
299
300 // get a list of all the classes in the hierarchy
301 String[] parts = in.getName().split("\\$");
302 List<ClassMapping> mappingsChain = Lists.newArrayList();
303
304 // get mappings for the outer class
305 ClassMapping outerClassMapping = this.classes.get(parts[0]);
306 mappingsChain.add(outerClassMapping);
307
308 for (int i = 1; i < parts.length; i++) {
309
310 // get mappings for the inner class
311 ClassMapping innerClassMapping = null;
312 if (outerClassMapping != null) {
313 innerClassMapping = this.direction.choose(
314 outerClassMapping.getInnerClassByObfSimple(parts[i]),
315 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
316 );
317 }
318 mappingsChain.add(innerClassMapping);
319 outerClassMapping = innerClassMapping;
320 }
321
322 assert (mappingsChain.size() == parts.length);
323 return mappingsChain;
324 }
325
326 public Mappings.EntryModifier getModifier(Entry entry) {
327 ClassMapping classMapping = findClassMapping(entry.getClassEntry());
328 if (classMapping != null && !entry.getName().equals("<clinit>")) {
329 if (entry instanceof ClassEntry)
330 return classMapping.getModifier();
331 else if (entry instanceof FieldEntry) {
332 FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType());
333 return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
334 } else if (entry instanceof BehaviorEntry) {
335 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature());
336 return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
337 } else
338 throw new Error("Unknown entry type: " + entry.getClass().getName());
339 } 57 }
340 return Mappings.EntryModifier.UNCHANGED; 58 throw new IllegalArgumentException("Cannot translate unknown entry type");
341 } 59 }
342} 60}
diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
new file mode 100644
index 0000000..9c0fe6d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
@@ -0,0 +1,240 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Maps;
15
16import java.util.Map;
17import java.util.function.Function;
18
19public class TypeDescriptor {
20
21 protected final String desc;
22
23 public TypeDescriptor(String desc) {
24 // don't deal with generics
25 // this is just for raw jvm types
26 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
27 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
28 }
29
30 this.desc = desc;
31 }
32
33 public static String parseFirst(String in) {
34
35 if (in == null || in.length() <= 0) {
36 throw new IllegalArgumentException("No desc to parse, input is empty!");
37 }
38
39 // read one desc from the input
40
41 char c = in.charAt(0);
42
43 // first check for void
44 if (c == 'V') {
45 return "V";
46 }
47
48 // then check for primitives
49 Primitive primitive = Primitive.get(c);
50 if (primitive != null) {
51 return in.substring(0, 1);
52 }
53
54 // then check for classes
55 if (c == 'L') {
56 return readClass(in);
57 }
58
59 // then check for templates
60 if (c == 'T') {
61 return readClass(in);
62 }
63
64 // then check for arrays
65 int dim = countArrayDimension(in);
66 if (dim > 0) {
67 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
68 return in.substring(0, dim + arrayType.length());
69 }
70
71 throw new IllegalArgumentException("don't know how to parse: " + in);
72 }
73
74 private static int countArrayDimension(String in) {
75 int i = 0;
76 while (i < in.length() && in.charAt(i) == '[')
77 i++;
78 return i;
79 }
80
81 private static String readClass(String in) {
82 // read all the characters in the buffer until we hit a ';'
83 // include the parameters too
84 StringBuilder buf = new StringBuilder();
85 int depth = 0;
86 for (int i = 0; i < in.length(); i++) {
87 char c = in.charAt(i);
88 buf.append(c);
89
90 if (c == '<') {
91 depth++;
92 } else if (c == '>') {
93 depth--;
94 } else if (depth == 0 && c == ';') {
95 return buf.toString();
96 }
97 }
98 return null;
99 }
100
101 public static TypeDescriptor of(String name) {
102 return new TypeDescriptor("L" + name + ";");
103 }
104
105 @Override
106 public String toString() {
107 return this.desc;
108 }
109
110 public boolean isVoid() {
111 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
112 }
113
114 public boolean isPrimitive() {
115 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
116 }
117
118 public Primitive getPrimitive() {
119 if (!isPrimitive()) {
120 throw new IllegalStateException("not a primitive");
121 }
122 return Primitive.get(this.desc.charAt(0));
123 }
124
125 public boolean isType() {
126 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
127 }
128
129 public ClassEntry getOwnerEntry() {
130 if (isType()) {
131 String name = this.desc.substring(1, this.desc.length() - 1);
132
133 int pos = name.indexOf('<');
134 if (pos >= 0) {
135 // remove the parameters from the class name
136 name = name.substring(0, pos);
137 }
138
139 return new ClassEntry(name);
140
141 } else if (isArray() && getArrayType().isType()) {
142 return getArrayType().getOwnerEntry();
143 } else {
144 throw new IllegalStateException("desc doesn't have a class");
145 }
146 }
147
148 public boolean isArray() {
149 return this.desc.charAt(0) == '[';
150 }
151
152 public int getArrayDimension() {
153 if (!isArray()) {
154 throw new IllegalStateException("not an array");
155 }
156 return countArrayDimension(this.desc);
157 }
158
159 public TypeDescriptor getArrayType() {
160 if (!isArray()) {
161 throw new IllegalStateException("not an array");
162 }
163 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length()));
164 }
165
166 public boolean containsType() {
167 return isType() || (isArray() && getArrayType().containsType());
168 }
169
170 @Override
171 public boolean equals(Object other) {
172 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
173 }
174
175 public boolean equals(TypeDescriptor other) {
176 return this.desc.equals(other.desc);
177 }
178
179 @Override
180 public int hashCode() {
181 return this.desc.hashCode();
182 }
183
184 public TypeDescriptor remap(Function<String, String> remapper) {
185 String desc = this.desc;
186 if (isType() || (isArray() && containsType())) {
187 String replacedName = remapper.apply(this.getOwnerEntry().getName());
188 if (replacedName != null) {
189 if (this.isType()) {
190 desc = "L" + replacedName + ";";
191 } else {
192 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
193 }
194 }
195 }
196 return new TypeDescriptor(desc);
197 }
198
199 private static String getArrayPrefix(int dimension) {
200 StringBuilder buf = new StringBuilder();
201 for (int i = 0; i < dimension; i++) {
202 buf.append("[");
203 }
204 return buf.toString();
205 }
206
207 public enum Primitive {
208 Byte('B'),
209 Character('C'),
210 Short('S'),
211 Integer('I'),
212 Long('J'),
213 Float('F'),
214 Double('D'),
215 Boolean('Z');
216
217 private static final Map<Character, Primitive> lookup;
218
219 static {
220 lookup = Maps.newTreeMap();
221 for (Primitive val : values()) {
222 lookup.put(val.getCode(), val);
223 }
224 }
225
226 private char code;
227
228 Primitive(char code) {
229 this.code = code;
230 }
231
232 public static Primitive get(char code) {
233 return lookup.get(code);
234 }
235
236 public char getCode() {
237 return this.code;
238 }
239 }
240}