summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gegy10002018-05-19 17:02:46 +0200
committerGravatar gegy10002018-05-19 17:02:46 +0200
commit2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 (patch)
tree14c8b1e806449ace1641a1dbafae162855f79670
parentFix build (diff)
downloadenigma-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.tar.gz
enigma-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.tar.xz
enigma-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.zip
Initial port to ASM
-rw-r--r--build.gradle3
-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
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfed.java6
-rw-r--r--src/test/java/cuchaz/enigma/TestEntryFactory.java28
-rw-r--r--src/test/java/cuchaz/enigma/TestInnerClasses.java47
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java78
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java111
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java37
-rw-r--r--src/test/java/cuchaz/enigma/TestMethodDescriptor.java247
-rw-r--r--src/test/java/cuchaz/enigma/TestSignature.java270
-rw-r--r--src/test/java/cuchaz/enigma/TestTokensConstructors.java90
-rw-r--r--src/test/java/cuchaz/enigma/TestTypeDescriptor.java243
93 files changed, 3028 insertions, 5153 deletions
diff --git a/build.gradle b/build.gradle
index 4669a2ac..c7db8ec5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -65,10 +65,9 @@ configurations {
65 65
66dependencies { 66dependencies {
67 compile 'com.google.guava:guava:21.+' 67 compile 'com.google.guava:guava:21.+'
68 compile 'org.javassist:javassist:3.21.0-GA'
69 compile 'org.bitbucket.mstrobel:procyon-compilertools:0.5.33.8-enigma' 68 compile 'org.bitbucket.mstrobel:procyon-compilertools:0.5.33.8-enigma'
70 compile 'com.google.code.gson:gson:2.8.1' 69 compile 'com.google.code.gson:gson:2.8.1'
71 70 compile "org.ow2.asm:asm-debug-all:5.1"
72 71
73 application 'de.sciss:syntaxpane:1.1.+' 72 application 'de.sciss:syntaxpane:1.1.+'
74 73
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index f546eb18..59eb1b66 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 1e99af2f..e1454c7e 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 2a2041a0..c91070fc 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 547d85ef..81814183 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 a2f1f909..00000000
--- 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 f2fb2f8d..d0e1ddbe 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 24e7cb0b..8fd71b7f 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 3761fca8..b972585a 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 75806c33..b0bcc91e 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 34d2eff1..3e467dba 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 00000000..97d6ffa2
--- /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 00000000..621bd33b
--- /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 00000000..552601b6
--- /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 87d3797d..00000000
--- 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 d0d0f2c5..972d4fe0 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 bacb1aac..6791b831 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 4f84dd09..f060ed9d 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 6556b2cf..37b40730 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 00000000..78ef722d
--- /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 b13415da..015eaac6 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 1b619164..2a62241d 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 a94a55b7..241b9f7a 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 26be05b4..3cd80ffd 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 00000000..0999abf0
--- /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 6ec576e0..9ed6db9f 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 d627fe91..64de788f 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 62a838d1..00000000
--- 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 1932730d..00000000
--- 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 9013d581..00000000
--- 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 3f819abd..00000000
--- 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 eaa6e901..00000000
--- 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 27d991a3..00000000
--- 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 aef35321..00000000
--- 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 058bb454..00000000
--- 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 985e792e..00000000
--- 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 10b0cb0c..00000000
--- 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 cc7fdbe8..00000000
--- 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 5c68d4af..00000000
--- 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 cc3b41bc..00000000
--- 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 4ac5a8b0..00000000
--- 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 0e359386..00000000
--- 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 51b3d2df..00000000
--- 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 4e632b94..00000000
--- 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 00000000..24bfe24d
--- /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 00000000..e40becc4
--- /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 f76dc897..97deaf3c 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 4a891cf4..5d9f7cbb 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 6d98743d..a5528a73 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 f80abba6..caa985c9 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 9154cc22..00000000
--- 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 04b4ebc7..dc1b02e7 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 788811ff..a49f8dd5 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 51751ca9..c782250d 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 801c4104..00000000
--- 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 20e51138..00000000
--- 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 00000000..12832673
--- /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 c79510b9..eb783e96 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 993bb64b..c20f6f5f 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 00000000..262c16c5
--- /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 0f1f5065..c118ac05 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 cd761b47..3c46a37e 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 00000000..cc677c55
--- /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 2bb5e3f7..dcfd0ff7 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 91ecd106..193c566e 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 cf78ca30..cc1ec9cc 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 172641bd..4d5be2f6 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 a0d43133..d1d5634a 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 ba1b258b..19299778 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 7126d2b6..e215a0fc 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 b0eb826e..95daa73f 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 dfe9e88a..e635fa18 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 00000000..d6a160d5
--- /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 00000000..210ada07
--- /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 9c3058c4..f8a5ff15 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 1524ce63..2f7fe53c 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 aa3dc4de..f178093d 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 33d930dc..93006564 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 00000000..2abc76c6
--- /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 78130d6b..00000000
--- 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 17e31876..4bbde548 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 8d464fc4..59bdf1a3 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 00000000..9c0fe6d5
--- /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}
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java
index e6c1b746..fbdebd61 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfed.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfed.java
@@ -12,6 +12,8 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.analysis.ParsedJar;
16import cuchaz.enigma.mapping.ReferencedEntryPool;
15import org.junit.BeforeClass; 17import org.junit.BeforeClass;
16import org.junit.Test; 18import org.junit.Test;
17 19
@@ -30,8 +32,8 @@ public class TestDeobfed {
30 public static void beforeClass() 32 public static void beforeClass()
31 throws Exception { 33 throws Exception {
32 jar = new JarFile("build/test-deobf/translation.jar"); 34 jar = new JarFile("build/test-deobf/translation.jar");
33 index = new JarIndex(); 35 index = new JarIndex(new ReferencedEntryPool());
34 index.indexJar(jar, true); 36 index.indexJar(new ParsedJar(jar), true);
35 } 37 }
36 38
37 @Test 39 @Test
diff --git a/src/test/java/cuchaz/enigma/TestEntryFactory.java b/src/test/java/cuchaz/enigma/TestEntryFactory.java
index 1c527f53..067dcbfe 100644
--- a/src/test/java/cuchaz/enigma/TestEntryFactory.java
+++ b/src/test/java/cuchaz/enigma/TestEntryFactory.java
@@ -25,7 +25,7 @@ public class TestEntryFactory {
25 } 25 }
26 26
27 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { 27 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) {
28 return new FieldEntry(classEntry, fieldName, new Type(fieldType)); 28 return new FieldEntry(classEntry, fieldName, new TypeDescriptor(fieldType));
29 } 29 }
30 30
31 public static MethodEntry newMethod(String className, String methodName, String methodSignature) { 31 public static MethodEntry newMethod(String className, String methodName, String methodSignature) {
@@ -33,30 +33,14 @@ public class TestEntryFactory {
33 } 33 }
34 34
35 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { 35 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) {
36 return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); 36 return new MethodEntry(classEntry, methodName, new MethodDescriptor(methodSignature));
37 } 37 }
38 38
39 public static ConstructorEntry newConstructor(String className, String signature) { 39 public static EntryReference<FieldEntry, MethodEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) {
40 return newConstructor(newClass(className), signature); 40 return new EntryReference<>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature));
41 } 41 }
42 42
43 public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { 43 public static EntryReference<MethodEntry, MethodEntry> newBehaviorReferenceByMethod(MethodEntry methodEntry, String callerClassName, String callerName, String callerSignature) {
44 return new ConstructorEntry(classEntry, new Signature(signature)); 44 return new EntryReference<>(methodEntry, "", newMethod(callerClassName, callerName, callerSignature));
45 }
46
47 public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) {
48 return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature));
49 }
50
51 public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) {
52 return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newConstructor(callerClassName, callerSignature));
53 }
54
55 public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) {
56 return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature));
57 }
58
59 public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) {
60 return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newConstructor(callerClassName, callerSignature));
61 } 45 }
62} 46}
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 38db0df9..30e127e6 100644
--- a/src/test/java/cuchaz/enigma/TestInnerClasses.java
+++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java
@@ -12,7 +12,9 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.analysis.ParsedJar;
15import cuchaz.enigma.mapping.ClassEntry; 16import cuchaz.enigma.mapping.ClassEntry;
17import cuchaz.enigma.mapping.ReferencedEntryPool;
16import org.junit.Test; 18import org.junit.Test;
17 19
18import java.util.jar.JarFile; 20import java.util.jar.JarFile;
@@ -26,16 +28,10 @@ import static org.hamcrest.Matchers.nullValue;
26 28
27public class TestInnerClasses { 29public class TestInnerClasses {
28 30
29 private static final ClassEntry AnonymousOuter = newClass("a");
30 private static final ClassEntry AnonymousInner = newClass("a$1");
31 private static final ClassEntry SimpleOuter = newClass("d"); 31 private static final ClassEntry SimpleOuter = newClass("d");
32 private static final ClassEntry SimpleInner = newClass("d$a"); 32 private static final ClassEntry SimpleInner = newClass("d$a");
33 private static final ClassEntry ConstructorArgsOuter = newClass("c"); 33 private static final ClassEntry ConstructorArgsOuter = newClass("c");
34 private static final ClassEntry ConstructorArgsInner = newClass("c$a"); 34 private static final ClassEntry ConstructorArgsInner = newClass("c$a");
35 private static final ClassEntry AnonymousWithScopeArgsOuter = newClass("b");
36 private static final ClassEntry AnonymousWithScopeArgsInner = newClass("b$1");
37 private static final ClassEntry AnonymousWithOuterAccessOuter = newClass("e");
38 private static final ClassEntry AnonymousWithOuterAccessInner = newClass("e$1");
39 private static final ClassEntry ClassTreeRoot = newClass("f"); 35 private static final ClassEntry ClassTreeRoot = newClass("f");
40 private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); 36 private static final ClassEntry ClassTreeLevel1 = newClass("f$a");
41 private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); 37 private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a");
@@ -45,9 +41,9 @@ public class TestInnerClasses {
45 41
46 public TestInnerClasses() 42 public TestInnerClasses()
47 throws Exception { 43 throws Exception {
48 index = new JarIndex(); 44 index = new JarIndex(new ReferencedEntryPool());
49 JarFile jar = new JarFile("build/test-obf/innerClasses.jar"); 45 JarFile jar = new JarFile("build/test-obf/innerClasses.jar");
50 index.indexJar(jar, true); 46 index.indexJar(new ParsedJar(jar), true);
51 deobfuscator = new Deobfuscator(jar); 47 deobfuscator = new Deobfuscator(jar);
52 } 48 }
53 49
@@ -55,43 +51,17 @@ public class TestInnerClasses {
55 public void simple() { 51 public void simple() {
56 assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter)); 52 assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter));
57 assertThat(index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner)); 53 assertThat(index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner));
58 assertThat(index.isAnonymousClass(SimpleInner), is(false));
59 decompile(SimpleOuter); 54 decompile(SimpleOuter);
60 } 55 }
61 56
62 @Test 57 @Test
63 public void anonymous() {
64 assertThat(index.getOuterClass(AnonymousInner), is(AnonymousOuter));
65 assertThat(index.getInnerClasses(AnonymousOuter), containsInAnyOrder(AnonymousInner));
66 assertThat(index.isAnonymousClass(AnonymousInner), is(true));
67 decompile(AnonymousOuter);
68 }
69
70 @Test
71 public void constructorArgs() { 58 public void constructorArgs() {
72 assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); 59 assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter));
73 assertThat(index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner)); 60 assertThat(index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner));
74 assertThat(index.isAnonymousClass(ConstructorArgsInner), is(false));
75 decompile(ConstructorArgsOuter); 61 decompile(ConstructorArgsOuter);
76 } 62 }
77 63
78 @Test 64 @Test
79 public void anonymousWithScopeArgs() {
80 assertThat(index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter));
81 assertThat(index.getInnerClasses(AnonymousWithScopeArgsOuter), containsInAnyOrder(AnonymousWithScopeArgsInner));
82 assertThat(index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true));
83 decompile(AnonymousWithScopeArgsOuter);
84 }
85
86 @Test
87 public void anonymousWithOuterAccess() {
88 assertThat(index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter));
89 assertThat(index.getInnerClasses(AnonymousWithOuterAccessOuter), containsInAnyOrder(AnonymousWithOuterAccessInner));
90 assertThat(index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true));
91 decompile(AnonymousWithOuterAccessOuter);
92 }
93
94 @Test
95 public void classTree() { 65 public void classTree() {
96 66
97 // root level 67 // root level
@@ -101,8 +71,7 @@ public class TestInnerClasses {
101 71
102 // level 1 72 // level 1
103 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 73 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
104 + "$" + ClassTreeLevel1.getInnermostClassName() 74 + "$" + ClassTreeLevel1.getInnermostClassName());
105 );
106 assertThat(index.containsObfClass(fullClassEntry), is(true)); 75 assertThat(index.containsObfClass(fullClassEntry), is(true));
107 assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot)); 76 assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot));
108 assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2)); 77 assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2));
@@ -110,8 +79,7 @@ public class TestInnerClasses {
110 // level 2 79 // level 2
111 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 80 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
112 + "$" + ClassTreeLevel1.getInnermostClassName() 81 + "$" + ClassTreeLevel1.getInnermostClassName()
113 + "$" + ClassTreeLevel2.getInnermostClassName() 82 + "$" + ClassTreeLevel2.getInnermostClassName());
114 );
115 assertThat(index.containsObfClass(fullClassEntry), is(true)); 83 assertThat(index.containsObfClass(fullClassEntry), is(true));
116 assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1)); 84 assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1));
117 assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3)); 85 assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3));
@@ -120,8 +88,7 @@ public class TestInnerClasses {
120 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 88 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
121 + "$" + ClassTreeLevel1.getInnermostClassName() 89 + "$" + ClassTreeLevel1.getInnermostClassName()
122 + "$" + ClassTreeLevel2.getInnermostClassName() 90 + "$" + ClassTreeLevel2.getInnermostClassName()
123 + "$" + ClassTreeLevel3.getInnermostClassName() 91 + "$" + ClassTreeLevel3.getInnermostClassName());
124 );
125 assertThat(index.containsObfClass(fullClassEntry), is(true)); 92 assertThat(index.containsObfClass(fullClassEntry), is(true));
126 assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2)); 93 assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2));
127 assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty())); 94 assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty()));
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
index edb859a7..b20b27b6 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
@@ -13,22 +13,20 @@ package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.analysis.JarIndex; 15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.mapping.BehaviorEntry; 16import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.mapping.ClassEntry; 17import cuchaz.enigma.mapping.ClassEntry;
18import cuchaz.enigma.mapping.MethodDefEntry;
19import cuchaz.enigma.mapping.MethodEntry;
20import cuchaz.enigma.mapping.ReferencedEntryPool;
18import org.junit.Test; 21import org.junit.Test;
19 22
20import java.io.File; 23import java.io.File;
21import java.util.Collection; 24import java.util.Collection;
22import java.util.jar.JarFile; 25import java.util.jar.JarFile;
23 26
24import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor; 27import static cuchaz.enigma.TestEntryFactory.*;
25import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
26import static cuchaz.enigma.TestEntryFactory.newClass;
27import static cuchaz.enigma.TestEntryFactory.newConstructor;
28import static org.hamcrest.MatcherAssert.assertThat; 28import static org.hamcrest.MatcherAssert.assertThat;
29import static org.hamcrest.Matchers.containsInAnyOrder; 29import static org.hamcrest.Matchers.*;
30import static org.hamcrest.Matchers.empty;
31import static org.hamcrest.Matchers.is;
32 30
33public class TestJarIndexConstructorReferences { 31public class TestJarIndexConstructorReferences {
34 32
@@ -41,90 +39,90 @@ public class TestJarIndexConstructorReferences {
41 private ClassEntry callerClass = newClass("b"); 39 private ClassEntry callerClass = newClass("b");
42 40
43 public TestJarIndexConstructorReferences() 41 public TestJarIndexConstructorReferences()
44 throws Exception { 42 throws Exception {
45 File jarFile = new File("build/test-obf/constructors.jar"); 43 File jarFile = new File("build/test-obf/constructors.jar");
46 index = new JarIndex(); 44 index = new JarIndex(new ReferencedEntryPool());
47 index.indexJar(new JarFile(jarFile), false); 45 index.indexJar(new ParsedJar(new JarFile(jarFile)), false);
48 } 46 }
49 47
50 @Test 48 @Test
51 public void obfEntries() { 49 public void obfEntries() {
52 assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, 50 assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass,
53 subClass, subsubClass, defaultClass, callerClass)); 51 subClass, subsubClass, defaultClass, callerClass));
54 } 52 }
55 53
56 @Test 54 @Test
57 @SuppressWarnings("unchecked") 55 @SuppressWarnings("unchecked")
58 public void baseDefault() { 56 public void baseDefault() {
59 BehaviorEntry source = newConstructor(baseClass, "()V"); 57 MethodEntry source = newMethod(baseClass, "<init>", "()V");
60 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = index.getBehaviorReferences(source); 58 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = index.getMethodReferences(source);
61 assertThat(references, containsInAnyOrder( 59 assertThat(references, containsInAnyOrder(
62 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), 60 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"),
63 newBehaviorReferenceByConstructor(source, subClass.getName(), "()V"), 61 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "()V"),
64 newBehaviorReferenceByConstructor(source, subClass.getName(), "(III)V") 62 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(III)V")
65 )); 63 ));
66 } 64 }
67 65
68 @Test 66 @Test
69 @SuppressWarnings("unchecked") 67 @SuppressWarnings("unchecked")
70 public void baseInt() { 68 public void baseInt() {
71 BehaviorEntry source = newConstructor(baseClass, "(I)V"); 69 MethodEntry source = newMethod(baseClass, "<init>", "(I)V");
72 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 70 assertThat(index.getMethodReferences(source), containsInAnyOrder(
73 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") 71 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V")
74 )); 72 ));
75 } 73 }
76 74
77 @Test 75 @Test
78 @SuppressWarnings("unchecked") 76 @SuppressWarnings("unchecked")
79 public void subDefault() { 77 public void subDefault() {
80 BehaviorEntry source = newConstructor(subClass, "()V"); 78 MethodEntry source = newMethod(subClass, "<init>", "()V");
81 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 79 assertThat(index.getMethodReferences(source), containsInAnyOrder(
82 newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"), 80 newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"),
83 newBehaviorReferenceByConstructor(source, subClass.getName(), "(I)V") 81 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(I)V")
84 )); 82 ));
85 } 83 }
86 84
87 @Test 85 @Test
88 @SuppressWarnings("unchecked") 86 @SuppressWarnings("unchecked")
89 public void subInt() { 87 public void subInt() {
90 BehaviorEntry source = newConstructor(subClass, "(I)V"); 88 MethodEntry source = newMethod(subClass, "<init>", "(I)V");
91 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 89 assertThat(index.getMethodReferences(source), containsInAnyOrder(
92 newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), 90 newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"),
93 newBehaviorReferenceByConstructor(source, subClass.getName(), "(II)V"), 91 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(II)V"),
94 newBehaviorReferenceByConstructor(source, subsubClass.getName(), "(I)V") 92 newBehaviorReferenceByMethod(source, subsubClass.getName(), "<init>", "(I)V")
95 )); 93 ));
96 } 94 }
97 95
98 @Test 96 @Test
99 @SuppressWarnings("unchecked") 97 @SuppressWarnings("unchecked")
100 public void subIntInt() { 98 public void subIntInt() {
101 BehaviorEntry source = newConstructor(subClass, "(II)V"); 99 MethodEntry source = newMethod(subClass, "<init>", "(II)V");
102 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 100 assertThat(index.getMethodReferences(source), containsInAnyOrder(
103 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") 101 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V")
104 )); 102 ));
105 } 103 }
106 104
107 @Test 105 @Test
108 public void subIntIntInt() { 106 public void subIntIntInt() {
109 BehaviorEntry source = newConstructor(subClass, "(III)V"); 107 MethodEntry source = newMethod(subClass, "<init>", "(III)V");
110 assertThat(index.getBehaviorReferences(source), is(empty())); 108 assertThat(index.getMethodReferences(source), is(empty()));
111 } 109 }
112 110
113 @Test 111 @Test
114 @SuppressWarnings("unchecked") 112 @SuppressWarnings("unchecked")
115 public void subsubInt() { 113 public void subsubInt() {
116 BehaviorEntry source = newConstructor(subsubClass, "(I)V"); 114 MethodEntry source = newMethod(subsubClass, "<init>", "(I)V");
117 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 115 assertThat(index.getMethodReferences(source), containsInAnyOrder(
118 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") 116 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V")
119 )); 117 ));
120 } 118 }
121 119
122 @Test 120 @Test
123 @SuppressWarnings("unchecked") 121 @SuppressWarnings("unchecked")
124 public void defaultConstructable() { 122 public void defaultConstructable() {
125 BehaviorEntry source = newConstructor(defaultClass, "()V"); 123 MethodEntry source = newMethod(defaultClass, "<init>", "()V");
126 assertThat(index.getBehaviorReferences(source), containsInAnyOrder( 124 assertThat(index.getMethodReferences(source), containsInAnyOrder(
127 newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V") 125 newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V")
128 )); 126 ));
129 } 127 }
130} 128}
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
index 62469780..d1c85964 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
@@ -11,14 +11,8 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.Access; 14import cuchaz.enigma.analysis.*;
15import cuchaz.enigma.analysis.EntryReference; 15import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.mapping.BehaviorEntry;
19import cuchaz.enigma.mapping.ClassEntry;
20import cuchaz.enigma.mapping.FieldEntry;
21import cuchaz.enigma.mapping.MethodEntry;
22import org.junit.Test; 16import org.junit.Test;
23 17
24import java.util.Collection; 18import java.util.Collection;
@@ -27,10 +21,7 @@ import java.util.jar.JarFile;
27 21
28import static cuchaz.enigma.TestEntryFactory.*; 22import static cuchaz.enigma.TestEntryFactory.*;
29import static org.hamcrest.MatcherAssert.assertThat; 23import static org.hamcrest.MatcherAssert.assertThat;
30import static org.hamcrest.Matchers.contains; 24import static org.hamcrest.Matchers.*;
31import static org.hamcrest.Matchers.containsInAnyOrder;
32import static org.hamcrest.Matchers.empty;
33import static org.hamcrest.Matchers.is;
34 25
35public class TestJarIndexInheritanceTree { 26public class TestJarIndexInheritanceTree {
36 27
@@ -45,15 +36,15 @@ public class TestJarIndexInheritanceTree {
45 private FieldEntry numThingsField = newField(subClassB, "a", "I"); 36 private FieldEntry numThingsField = newField(subClassB, "a", "I");
46 37
47 public TestJarIndexInheritanceTree() 38 public TestJarIndexInheritanceTree()
48 throws Exception { 39 throws Exception {
49 index = new JarIndex(); 40 index = new JarIndex(new ReferencedEntryPool());
50 index.indexJar(new JarFile("build/test-obf/inheritanceTree.jar"), false); 41 index.indexJar(new ParsedJar(new JarFile("build/test-obf/inheritanceTree.jar")), false);
51 } 42 }
52 43
53 @Test 44 @Test
54 public void obfEntries() { 45 public void obfEntries() {
55 assertThat(index.getObfClassEntries(), containsInAnyOrder( 46 assertThat(index.getObfClassEntries(), containsInAnyOrder(
56 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB 47 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB
57 )); 48 ));
58 } 49 }
59 50
@@ -98,33 +89,33 @@ public class TestJarIndexInheritanceTree {
98 // getName() 89 // getName()
99 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;")); 90 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;"));
100 assertThat(entries, containsInAnyOrder( 91 assertThat(entries, containsInAnyOrder(
101 newMethod(baseClass, "a", "()Ljava/lang/String;"), 92 newMethod(baseClass, "a", "()Ljava/lang/String;"),
102 newMethod(subClassAA, "a", "()Ljava/lang/String;") 93 newMethod(subClassAA, "a", "()Ljava/lang/String;")
103 )); 94 ));
104 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()Ljava/lang/String;")); 95 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()Ljava/lang/String;"));
105 assertThat(entries, containsInAnyOrder( 96 assertThat(entries, containsInAnyOrder(
106 newMethod(baseClass, "a", "()Ljava/lang/String;"), 97 newMethod(baseClass, "a", "()Ljava/lang/String;"),
107 newMethod(subClassAA, "a", "()Ljava/lang/String;") 98 newMethod(subClassAA, "a", "()Ljava/lang/String;")
108 )); 99 ));
109 100
110 // doBaseThings() 101 // doBaseThings()
111 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V")); 102 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V"));
112 assertThat(entries, containsInAnyOrder( 103 assertThat(entries, containsInAnyOrder(
113 newMethod(baseClass, "a", "()V"), 104 newMethod(baseClass, "a", "()V"),
114 newMethod(subClassAA, "a", "()V"), 105 newMethod(subClassAA, "a", "()V"),
115 newMethod(subClassB, "a", "()V") 106 newMethod(subClassB, "a", "()V")
116 )); 107 ));
117 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()V")); 108 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()V"));
118 assertThat(entries, containsInAnyOrder( 109 assertThat(entries, containsInAnyOrder(
119 newMethod(baseClass, "a", "()V"), 110 newMethod(baseClass, "a", "()V"),
120 newMethod(subClassAA, "a", "()V"), 111 newMethod(subClassAA, "a", "()V"),
121 newMethod(subClassB, "a", "()V") 112 newMethod(subClassB, "a", "()V")
122 )); 113 ));
123 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "a", "()V")); 114 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "a", "()V"));
124 assertThat(entries, containsInAnyOrder( 115 assertThat(entries, containsInAnyOrder(
125 newMethod(baseClass, "a", "()V"), 116 newMethod(baseClass, "a", "()V"),
126 newMethod(subClassAA, "a", "()V"), 117 newMethod(subClassAA, "a", "()V"),
127 newMethod(subClassB, "a", "()V") 118 newMethod(subClassB, "a", "()V")
128 )); 119 ));
129 120
130 // doBThings 121 // doBThings
@@ -135,20 +126,20 @@ public class TestJarIndexInheritanceTree {
135 @Test 126 @Test
136 @SuppressWarnings("unchecked") 127 @SuppressWarnings("unchecked")
137 public void fieldReferences() { 128 public void fieldReferences() {
138 Collection<EntryReference<FieldEntry, BehaviorEntry>> references; 129 Collection<EntryReference<FieldEntry, MethodDefEntry>> references;
139 130
140 // name 131 // name
141 references = index.getFieldReferences(nameField); 132 references = index.getFieldReferences(nameField);
142 assertThat(references, containsInAnyOrder( 133 assertThat(references, containsInAnyOrder(
143 newFieldReferenceByConstructor(nameField, baseClass.getName(), "(Ljava/lang/String;)V"), 134 newFieldReferenceByMethod(nameField, baseClass.getName(), "<init>", "(Ljava/lang/String;)V"),
144 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") 135 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;")
145 )); 136 ));
146 137
147 // numThings 138 // numThings
148 references = index.getFieldReferences(numThingsField); 139 references = index.getFieldReferences(numThingsField);
149 assertThat(references, containsInAnyOrder( 140 assertThat(references, containsInAnyOrder(
150 newFieldReferenceByConstructor(numThingsField, subClassB.getName(), "()V"), 141 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "<init>", "()V"),
151 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") 142 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V")
152 )); 143 ));
153 } 144 }
154 145
@@ -156,37 +147,37 @@ public class TestJarIndexInheritanceTree {
156 @SuppressWarnings("unchecked") 147 @SuppressWarnings("unchecked")
157 public void behaviorReferences() { 148 public void behaviorReferences() {
158 149
159 BehaviorEntry source; 150 MethodEntry source;
160 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references; 151 Collection<EntryReference<MethodEntry, MethodDefEntry>> references;
161 152
162 // baseClass constructor 153 // baseClass constructor
163 source = newConstructor(baseClass, "(Ljava/lang/String;)V"); 154 source = newMethod(baseClass, "<init>", "(Ljava/lang/String;)V");
164 references = index.getBehaviorReferences(source); 155 references = index.getMethodReferences(source);
165 assertThat(references, containsInAnyOrder( 156 assertThat(references, containsInAnyOrder(
166 newBehaviorReferenceByConstructor(source, subClassA.getName(), "(Ljava/lang/String;)V"), 157 newBehaviorReferenceByMethod(source, subClassA.getName(), "<init>", "(Ljava/lang/String;)V"),
167 newBehaviorReferenceByConstructor(source, subClassB.getName(), "()V") 158 newBehaviorReferenceByMethod(source, subClassB.getName(), "<init>", "()V")
168 )); 159 ));
169 160
170 // subClassA constructor 161 // subClassA constructor
171 source = newConstructor(subClassA, "(Ljava/lang/String;)V"); 162 source = newMethod(subClassA, "<init>", "(Ljava/lang/String;)V");
172 references = index.getBehaviorReferences(source); 163 references = index.getMethodReferences(source);
173 assertThat(references, containsInAnyOrder( 164 assertThat(references, containsInAnyOrder(
174 newBehaviorReferenceByConstructor(source, subClassAA.getName(), "()V") 165 newBehaviorReferenceByMethod(source, subClassAA.getName(), "<init>", "()V")
175 )); 166 ));
176 167
177 // baseClass.getName() 168 // baseClass.getName()
178 source = newMethod(baseClass, "a", "()Ljava/lang/String;"); 169 source = newMethod(baseClass, "a", "()Ljava/lang/String;");
179 references = index.getBehaviorReferences(source); 170 references = index.getMethodReferences(source);
180 assertThat(references, containsInAnyOrder( 171 assertThat(references, containsInAnyOrder(
181 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), 172 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"),
182 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") 173 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V")
183 )); 174 ));
184 175
185 // subclassAA.getName() 176 // subclassAA.getName()
186 source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); 177 source = newMethod(subClassAA, "a", "()Ljava/lang/String;");
187 references = index.getBehaviorReferences(source); 178 references = index.getMethodReferences(source);
188 assertThat(references, containsInAnyOrder( 179 assertThat(references, containsInAnyOrder(
189 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") 180 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V")
190 )); 181 ));
191 } 182 }
192 183
@@ -205,22 +196,22 @@ public class TestJarIndexInheritanceTree {
205 196
206 // methods 197 // methods
207 // getName() 198 // getName()
208 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true)); 199 assertThat(index.containsObfMethod(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true));
209 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false)); 200 assertThat(index.containsObfMethod(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false));
210 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true)); 201 assertThat(index.containsObfMethod(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true));
211 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false)); 202 assertThat(index.containsObfMethod(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false));
212 203
213 // doBaseThings() 204 // doBaseThings()
214 assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()V")), is(true)); 205 assertThat(index.containsObfMethod(newMethod(baseClass, "a", "()V")), is(true));
215 assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()V")), is(false)); 206 assertThat(index.containsObfMethod(newMethod(subClassA, "a", "()V")), is(false));
216 assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()V")), is(true)); 207 assertThat(index.containsObfMethod(newMethod(subClassAA, "a", "()V")), is(true));
217 assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()V")), is(true)); 208 assertThat(index.containsObfMethod(newMethod(subClassB, "a", "()V")), is(true));
218 209
219 // doBThings() 210 // doBThings()
220 assertThat(index.containsObfBehavior(newMethod(baseClass, "b", "()V")), is(false)); 211 assertThat(index.containsObfMethod(newMethod(baseClass, "b", "()V")), is(false));
221 assertThat(index.containsObfBehavior(newMethod(subClassA, "b", "()V")), is(false)); 212 assertThat(index.containsObfMethod(newMethod(subClassA, "b", "()V")), is(false));
222 assertThat(index.containsObfBehavior(newMethod(subClassAA, "b", "()V")), is(false)); 213 assertThat(index.containsObfMethod(newMethod(subClassAA, "b", "()V")), is(false));
223 assertThat(index.containsObfBehavior(newMethod(subClassB, "b", "()V")), is(true)); 214 assertThat(index.containsObfMethod(newMethod(subClassB, "b", "()V")), is(true));
224 215
225 } 216 }
226} 217}
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
index 6cab1c84..d03f3fb4 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -28,16 +28,16 @@ public class TestJarIndexLoneClass {
28 private JarIndex index; 28 private JarIndex index;
29 29
30 public TestJarIndexLoneClass() 30 public TestJarIndexLoneClass()
31 throws Exception { 31 throws Exception {
32 index = new JarIndex(); 32 index = new JarIndex(new ReferencedEntryPool());
33 index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false); 33 index.indexJar(new ParsedJar(new JarFile("build/test-obf/loneClass.jar")), false);
34 } 34 }
35 35
36 @Test 36 @Test
37 public void obfEntries() { 37 public void obfEntries() {
38 assertThat(index.getObfClassEntries(), containsInAnyOrder( 38 assertThat(index.getObfClassEntries(), containsInAnyOrder(
39 newClass("cuchaz/enigma/inputs/Keep"), 39 newClass("cuchaz/enigma/inputs/Keep"),
40 newClass("a") 40 newClass("a")
41 )); 41 ));
42 } 42 }
43 43
@@ -61,7 +61,7 @@ public class TestJarIndexLoneClass {
61 61
62 @Test 62 @Test
63 public void classInheritance() { 63 public void classInheritance() {
64 ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a")); 64 ClassInheritanceTreeNode node = index.getClassInheritance(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a"));
65 assertThat(node, is(not(nullValue()))); 65 assertThat(node, is(not(nullValue())));
66 assertThat(node.getObfClassName(), is("a")); 66 assertThat(node.getObfClassName(), is("a"));
67 assertThat(node.getChildCount(), is(0)); 67 assertThat(node.getChildCount(), is(0));
@@ -70,7 +70,7 @@ public class TestJarIndexLoneClass {
70 @Test 70 @Test
71 public void methodInheritance() { 71 public void methodInheritance() {
72 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 72 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
73 MethodInheritanceTreeNode node = index.getMethodInheritance(new Translator(), source); 73 MethodInheritanceTreeNode node = index.getMethodInheritance(new DirectionalTranslator(new ReferencedEntryPool()), source);
74 assertThat(node, is(not(nullValue()))); 74 assertThat(node, is(not(nullValue())));
75 assertThat(node.getMethodEntry(), is(source)); 75 assertThat(node.getMethodEntry(), is(source));
76 assertThat(node.getChildCount(), is(0)); 76 assertThat(node.getChildCount(), is(0));
@@ -78,21 +78,21 @@ public class TestJarIndexLoneClass {
78 78
79 @Test 79 @Test
80 public void classImplementations() { 80 public void classImplementations() {
81 ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a")); 81 ClassImplementationsTreeNode node = index.getClassImplementations(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a"));
82 assertThat(node, is(nullValue())); 82 assertThat(node, is(nullValue()));
83 } 83 }
84 84
85 @Test 85 @Test
86 public void methodImplementations() { 86 public void methodImplementations() {
87 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 87 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
88 assertThat(index.getMethodImplementations(new Translator(), source), is(empty())); 88 assertThat(index.getMethodImplementations(new DirectionalTranslator(new ReferencedEntryPool()), source), is(empty()));
89 } 89 }
90 90
91 @Test 91 @Test
92 public void relatedMethodImplementations() { 92 public void relatedMethodImplementations() {
93 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); 93 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;"));
94 assertThat(entries, containsInAnyOrder( 94 assertThat(entries, containsInAnyOrder(
95 newMethod("a", "a", "()Ljava/lang/String;") 95 newMethod("a", "a", "()Ljava/lang/String;")
96 )); 96 ));
97 } 97 }
98 98
@@ -100,16 +100,16 @@ public class TestJarIndexLoneClass {
100 @SuppressWarnings("unchecked") 100 @SuppressWarnings("unchecked")
101 public void fieldReferences() { 101 public void fieldReferences() {
102 FieldEntry source = newField("a", "a", "Ljava/lang/String;"); 102 FieldEntry source = newField("a", "a", "Ljava/lang/String;");
103 Collection<EntryReference<FieldEntry, BehaviorEntry>> references = index.getFieldReferences(source); 103 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getFieldReferences(source);
104 assertThat(references, containsInAnyOrder( 104 assertThat(references, containsInAnyOrder(
105 newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"), 105 newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"),
106 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") 106 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")
107 )); 107 ));
108 } 108 }
109 109
110 @Test 110 @Test
111 public void behaviorReferences() { 111 public void behaviorReferences() {
112 assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); 112 assertThat(index.getMethodReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty()));
113 } 113 }
114 114
115 @Test 115 @Test
@@ -123,11 +123,6 @@ public class TestJarIndexLoneClass {
123 } 123 }
124 124
125 @Test 125 @Test
126 public void isAnonymousClass() {
127 assertThat(index.isAnonymousClass(newClass("a")), is(false));
128 }
129
130 @Test
131 public void interfaces() { 126 public void interfaces() {
132 assertThat(index.getInterfaces("a"), is(empty())); 127 assertThat(index.getInterfaces("a"), is(empty()));
133 } 128 }
@@ -149,7 +144,7 @@ public class TestJarIndexLoneClass {
149 assertThat(index.containsObfField(newField("a", "a", "Ljava/lang/String;")), is(true)); 144 assertThat(index.containsObfField(newField("a", "a", "Ljava/lang/String;")), is(true));
150 assertThat(index.containsObfField(newField("a", "b", "Ljava/lang/String;")), is(false)); 145 assertThat(index.containsObfField(newField("a", "b", "Ljava/lang/String;")), is(false));
151 assertThat(index.containsObfField(newField("a", "a", "LFoo;")), is(false)); 146 assertThat(index.containsObfField(newField("a", "a", "LFoo;")), is(false));
152 assertThat(index.containsObfBehavior(newMethod("a", "a", "()Ljava/lang/String;")), is(true)); 147 assertThat(index.containsObfMethod(newMethod("a", "a", "()Ljava/lang/String;")), is(true));
153 assertThat(index.containsObfBehavior(newMethod("a", "b", "()Ljava/lang/String;")), is(false)); 148 assertThat(index.containsObfMethod(newMethod("a", "b", "()Ljava/lang/String;")), is(false));
154 } 149 }
155} 150}
diff --git a/src/test/java/cuchaz/enigma/TestMethodDescriptor.java b/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
new file mode 100644
index 00000000..48c46e52
--- /dev/null
+++ b/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
@@ -0,0 +1,247 @@
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 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma;
13
14import cuchaz.enigma.mapping.MethodDescriptor;
15import cuchaz.enigma.mapping.TypeDescriptor;
16import org.junit.Test;
17
18import static org.hamcrest.MatcherAssert.assertThat;
19import static org.hamcrest.Matchers.*;
20
21public class TestMethodDescriptor {
22
23 @Test
24 public void easiest() {
25 final MethodDescriptor sig = new MethodDescriptor("()V");
26 assertThat(sig.getArgumentDescs(), is(empty()));
27 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
28 }
29
30 @Test
31 public void primitives() {
32 {
33 final MethodDescriptor sig = new MethodDescriptor("(I)V");
34 assertThat(sig.getArgumentDescs(), contains(
35 new TypeDescriptor("I")
36 ));
37 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
38 }
39 {
40 final MethodDescriptor sig = new MethodDescriptor("(I)I");
41 assertThat(sig.getArgumentDescs(), contains(
42 new TypeDescriptor("I")
43 ));
44 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("I")));
45 }
46 {
47 final MethodDescriptor sig = new MethodDescriptor("(IBCJ)Z");
48 assertThat(sig.getArgumentDescs(), contains(
49 new TypeDescriptor("I"),
50 new TypeDescriptor("B"),
51 new TypeDescriptor("C"),
52 new TypeDescriptor("J")
53 ));
54 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z")));
55 }
56 }
57
58 @Test
59 public void classes() {
60 {
61 final MethodDescriptor sig = new MethodDescriptor("([LFoo;)V");
62 assertThat(sig.getArgumentDescs().size(), is(1));
63 assertThat(sig.getArgumentDescs().get(0), is(new TypeDescriptor("[LFoo;")));
64 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
65 }
66 {
67 final MethodDescriptor sig = new MethodDescriptor("(LFoo;)LBar;");
68 assertThat(sig.getArgumentDescs(), contains(
69 new TypeDescriptor("LFoo;")
70 ));
71 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;")));
72 }
73 {
74 final MethodDescriptor sig = new MethodDescriptor("(LFoo;LMoo;LZoo;)LBar;");
75 assertThat(sig.getArgumentDescs(), contains(
76 new TypeDescriptor("LFoo;"),
77 new TypeDescriptor("LMoo;"),
78 new TypeDescriptor("LZoo;")
79 ));
80 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;")));
81 }
82 }
83
84 @Test
85 public void arrays() {
86 {
87 final MethodDescriptor sig = new MethodDescriptor("([I)V");
88 assertThat(sig.getArgumentDescs(), contains(
89 new TypeDescriptor("[I")
90 ));
91 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
92 }
93 {
94 final MethodDescriptor sig = new MethodDescriptor("([I)[J");
95 assertThat(sig.getArgumentDescs(), contains(
96 new TypeDescriptor("[I")
97 ));
98 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[J")));
99 }
100 {
101 final MethodDescriptor sig = new MethodDescriptor("([I[Z[F)[D");
102 assertThat(sig.getArgumentDescs(), contains(
103 new TypeDescriptor("[I"),
104 new TypeDescriptor("[Z"),
105 new TypeDescriptor("[F")
106 ));
107 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[D")));
108 }
109 }
110
111 @Test
112 public void mixed() {
113 {
114 final MethodDescriptor sig = new MethodDescriptor("(I[JLFoo;)Z");
115 assertThat(sig.getArgumentDescs(), contains(
116 new TypeDescriptor("I"),
117 new TypeDescriptor("[J"),
118 new TypeDescriptor("LFoo;")
119 ));
120 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z")));
121 }
122 {
123 final MethodDescriptor sig = new MethodDescriptor("(III)[LFoo;");
124 assertThat(sig.getArgumentDescs(), contains(
125 new TypeDescriptor("I"),
126 new TypeDescriptor("I"),
127 new TypeDescriptor("I")
128 ));
129 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[LFoo;")));
130 }
131 }
132
133 @Test
134 public void replaceClasses() {
135 {
136 final MethodDescriptor oldSig = new MethodDescriptor("()V");
137 final MethodDescriptor sig = oldSig.remap(s -> null);
138 assertThat(sig.getArgumentDescs(), is(empty()));
139 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
140 }
141 {
142 final MethodDescriptor oldSig = new MethodDescriptor("(IJLFoo;)V");
143 final MethodDescriptor sig = oldSig.remap(s -> null);
144 assertThat(sig.getArgumentDescs(), contains(
145 new TypeDescriptor("I"),
146 new TypeDescriptor("J"),
147 new TypeDescriptor("LFoo;")
148 ));
149 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V")));
150 }
151 {
152 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;");
153 final MethodDescriptor sig = oldSig.remap(s -> {
154 if (s.equals("Foo")) {
155 return "Bar";
156 }
157 return null;
158 });
159 assertThat(sig.getArgumentDescs(), contains(
160 new TypeDescriptor("LBar;"),
161 new TypeDescriptor("LBar;")
162 ));
163 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LMoo;")));
164 }
165 {
166 final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;");
167 final MethodDescriptor sig = oldSig.remap(s -> {
168 if (s.equals("Moo")) {
169 return "Cow";
170 }
171 return null;
172 });
173 assertThat(sig.getArgumentDescs(), contains(
174 new TypeDescriptor("LFoo;"),
175 new TypeDescriptor("LBar;")
176 ));
177 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LCow;")));
178 }
179 }
180
181 @Test
182 public void replaceArrayClasses() {
183 {
184 final MethodDescriptor oldSig = new MethodDescriptor("([LFoo;)[[[LBar;");
185 final MethodDescriptor sig = oldSig.remap(s -> {
186 if (s.equals("Foo")) {
187 return "Food";
188 } else if (s.equals("Bar")) {
189 return "Beer";
190 }
191 return null;
192 });
193 assertThat(sig.getArgumentDescs(), contains(
194 new TypeDescriptor("[LFood;")
195 ));
196 assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[[[LBeer;")));
197 }
198 }
199
200 @Test
201 public void equals() {
202
203 // base
204 assertThat(new MethodDescriptor("()V"), is(new MethodDescriptor("()V")));
205
206 // arguments
207 assertThat(new MethodDescriptor("(I)V"), is(new MethodDescriptor("(I)V")));
208 assertThat(new MethodDescriptor("(ZIZ)V"), is(new MethodDescriptor("(ZIZ)V")));
209 assertThat(new MethodDescriptor("(LFoo;)V"), is(new MethodDescriptor("(LFoo;)V")));
210 assertThat(new MethodDescriptor("(LFoo;LBar;)V"), is(new MethodDescriptor("(LFoo;LBar;)V")));
211 assertThat(new MethodDescriptor("([I)V"), is(new MethodDescriptor("([I)V")));
212 assertThat(new MethodDescriptor("([[D[[[J)V"), is(new MethodDescriptor("([[D[[[J)V")));
213
214 assertThat(new MethodDescriptor("()V"), is(not(new MethodDescriptor("(I)V"))));
215 assertThat(new MethodDescriptor("(I)V"), is(not(new MethodDescriptor("()V"))));
216 assertThat(new MethodDescriptor("(IJ)V"), is(not(new MethodDescriptor("(JI)V"))));
217 assertThat(new MethodDescriptor("([[Z)V"), is(not(new MethodDescriptor("([[LFoo;)V"))));
218 assertThat(new MethodDescriptor("(LFoo;LBar;)V"), is(not(new MethodDescriptor("(LFoo;LCow;)V"))));
219 assertThat(new MethodDescriptor("([LFoo;LBar;)V"), is(not(new MethodDescriptor("(LFoo;LCow;)V"))));
220
221 // return desc
222 assertThat(new MethodDescriptor("()I"), is(new MethodDescriptor("()I")));
223 assertThat(new MethodDescriptor("()Z"), is(new MethodDescriptor("()Z")));
224 assertThat(new MethodDescriptor("()[D"), is(new MethodDescriptor("()[D")));
225 assertThat(new MethodDescriptor("()[[[Z"), is(new MethodDescriptor("()[[[Z")));
226 assertThat(new MethodDescriptor("()LFoo;"), is(new MethodDescriptor("()LFoo;")));
227 assertThat(new MethodDescriptor("()[LFoo;"), is(new MethodDescriptor("()[LFoo;")));
228
229 assertThat(new MethodDescriptor("()I"), is(not(new MethodDescriptor("()Z"))));
230 assertThat(new MethodDescriptor("()Z"), is(not(new MethodDescriptor("()I"))));
231 assertThat(new MethodDescriptor("()[D"), is(not(new MethodDescriptor("()[J"))));
232 assertThat(new MethodDescriptor("()[[[Z"), is(not(new MethodDescriptor("()[[Z"))));
233 assertThat(new MethodDescriptor("()LFoo;"), is(not(new MethodDescriptor("()LBar;"))));
234 assertThat(new MethodDescriptor("()[LFoo;"), is(not(new MethodDescriptor("()[LBar;"))));
235 }
236
237 @Test
238 public void testToString() {
239 assertThat(new MethodDescriptor("()V").toString(), is("()V"));
240 assertThat(new MethodDescriptor("(I)V").toString(), is("(I)V"));
241 assertThat(new MethodDescriptor("(ZIZ)V").toString(), is("(ZIZ)V"));
242 assertThat(new MethodDescriptor("(LFoo;)V").toString(), is("(LFoo;)V"));
243 assertThat(new MethodDescriptor("(LFoo;LBar;)V").toString(), is("(LFoo;LBar;)V"));
244 assertThat(new MethodDescriptor("([I)V").toString(), is("([I)V"));
245 assertThat(new MethodDescriptor("([[D[[[J)V").toString(), is("([[D[[[J)V"));
246 }
247}
diff --git a/src/test/java/cuchaz/enigma/TestSignature.java b/src/test/java/cuchaz/enigma/TestSignature.java
deleted file mode 100644
index 534b43ae..00000000
--- a/src/test/java/cuchaz/enigma/TestSignature.java
+++ /dev/null
@@ -1,270 +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 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma;
13
14import cuchaz.enigma.mapping.ClassNameReplacer;
15import cuchaz.enigma.mapping.Signature;
16import cuchaz.enigma.mapping.Type;
17import org.junit.Test;
18
19import static org.hamcrest.MatcherAssert.assertThat;
20import static org.hamcrest.Matchers.contains;
21import static org.hamcrest.Matchers.empty;
22import static org.hamcrest.Matchers.is;
23import static org.hamcrest.Matchers.not;
24
25public class TestSignature {
26
27 @Test
28 public void easiest() {
29 final Signature sig = new Signature("()V");
30 assertThat(sig.getArgumentTypes(), is(empty()));
31 assertThat(sig.getReturnType(), is(new Type("V")));
32 }
33
34 @Test
35 public void primitives() {
36 {
37 final Signature sig = new Signature("(I)V");
38 assertThat(sig.getArgumentTypes(), contains(
39 new Type("I")
40 ));
41 assertThat(sig.getReturnType(), is(new Type("V")));
42 }
43 {
44 final Signature sig = new Signature("(I)I");
45 assertThat(sig.getArgumentTypes(), contains(
46 new Type("I")
47 ));
48 assertThat(sig.getReturnType(), is(new Type("I")));
49 }
50 {
51 final Signature sig = new Signature("(IBCJ)Z");
52 assertThat(sig.getArgumentTypes(), contains(
53 new Type("I"),
54 new Type("B"),
55 new Type("C"),
56 new Type("J")
57 ));
58 assertThat(sig.getReturnType(), is(new Type("Z")));
59 }
60 }
61
62 @Test
63 public void classes() {
64 {
65 final Signature sig = new Signature("([LFoo;)V");
66 assertThat(sig.getArgumentTypes().size(), is(1));
67 assertThat(sig.getArgumentTypes().get(0), is(new Type("[LFoo;")));
68 assertThat(sig.getReturnType(), is(new Type("V")));
69 }
70 {
71 final Signature sig = new Signature("(LFoo;)LBar;");
72 assertThat(sig.getArgumentTypes(), contains(
73 new Type("LFoo;")
74 ));
75 assertThat(sig.getReturnType(), is(new Type("LBar;")));
76 }
77 {
78 final Signature sig = new Signature("(LFoo;LMoo;LZoo;)LBar;");
79 assertThat(sig.getArgumentTypes(), contains(
80 new Type("LFoo;"),
81 new Type("LMoo;"),
82 new Type("LZoo;")
83 ));
84 assertThat(sig.getReturnType(), is(new Type("LBar;")));
85 }
86 }
87
88 @Test
89 public void arrays() {
90 {
91 final Signature sig = new Signature("([I)V");
92 assertThat(sig.getArgumentTypes(), contains(
93 new Type("[I")
94 ));
95 assertThat(sig.getReturnType(), is(new Type("V")));
96 }
97 {
98 final Signature sig = new Signature("([I)[J");
99 assertThat(sig.getArgumentTypes(), contains(
100 new Type("[I")
101 ));
102 assertThat(sig.getReturnType(), is(new Type("[J")));
103 }
104 {
105 final Signature sig = new Signature("([I[Z[F)[D");
106 assertThat(sig.getArgumentTypes(), contains(
107 new Type("[I"),
108 new Type("[Z"),
109 new Type("[F")
110 ));
111 assertThat(sig.getReturnType(), is(new Type("[D")));
112 }
113 }
114
115 @Test
116 public void mixed() {
117 {
118 final Signature sig = new Signature("(I[JLFoo;)Z");
119 assertThat(sig.getArgumentTypes(), contains(
120 new Type("I"),
121 new Type("[J"),
122 new Type("LFoo;")
123 ));
124 assertThat(sig.getReturnType(), is(new Type("Z")));
125 }
126 {
127 final Signature sig = new Signature("(III)[LFoo;");
128 assertThat(sig.getArgumentTypes(), contains(
129 new Type("I"),
130 new Type("I"),
131 new Type("I")
132 ));
133 assertThat(sig.getReturnType(), is(new Type("[LFoo;")));
134 }
135 }
136
137 @Test
138 public void replaceClasses() {
139 {
140 final Signature oldSig = new Signature("()V");
141 final Signature sig = new Signature(oldSig, new ClassNameReplacer() {
142 @Override
143 public String replace(String val) {
144 return null;
145 }
146 });
147 assertThat(sig.getArgumentTypes(), is(empty()));
148 assertThat(sig.getReturnType(), is(new Type("V")));
149 }
150 {
151 final Signature oldSig = new Signature("(IJLFoo;)V");
152 final Signature sig = new Signature(oldSig, new ClassNameReplacer() {
153 @Override
154 public String replace(String val) {
155 return null;
156 }
157 });
158 assertThat(sig.getArgumentTypes(), contains(
159 new Type("I"),
160 new Type("J"),
161 new Type("LFoo;")
162 ));
163 assertThat(sig.getReturnType(), is(new Type("V")));
164 }
165 {
166 final Signature oldSig = new Signature("(LFoo;LBar;)LMoo;");
167 final Signature sig = new Signature(oldSig, new ClassNameReplacer() {
168 @Override
169 public String replace(String val) {
170 if (val.equals("Foo")) {
171 return "Bar";
172 }
173 return null;
174 }
175 });
176 assertThat(sig.getArgumentTypes(), contains(
177 new Type("LBar;"),
178 new Type("LBar;")
179 ));
180 assertThat(sig.getReturnType(), is(new Type("LMoo;")));
181 }
182 {
183 final Signature oldSig = new Signature("(LFoo;LBar;)LMoo;");
184 final Signature sig = new Signature(oldSig, new ClassNameReplacer() {
185 @Override
186 public String replace(String val) {
187 if (val.equals("Moo")) {
188 return "Cow";
189 }
190 return null;
191 }
192 });
193 assertThat(sig.getArgumentTypes(), contains(
194 new Type("LFoo;"),
195 new Type("LBar;")
196 ));
197 assertThat(sig.getReturnType(), is(new Type("LCow;")));
198 }
199 }
200
201 @Test
202 public void replaceArrayClasses() {
203 {
204 final Signature oldSig = new Signature("([LFoo;)[[[LBar;");
205 final Signature sig = new Signature(oldSig, new ClassNameReplacer() {
206 @Override
207 public String replace(String val) {
208 if (val.equals("Foo")) {
209 return "Food";
210 } else if (val.equals("Bar")) {
211 return "Beer";
212 }
213 return null;
214 }
215 });
216 assertThat(sig.getArgumentTypes(), contains(
217 new Type("[LFood;")
218 ));
219 assertThat(sig.getReturnType(), is(new Type("[[[LBeer;")));
220 }
221 }
222
223 @Test
224 public void equals() {
225
226 // base
227 assertThat(new Signature("()V"), is(new Signature("()V")));
228
229 // arguments
230 assertThat(new Signature("(I)V"), is(new Signature("(I)V")));
231 assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V")));
232 assertThat(new Signature("(LFoo;)V"), is(new Signature("(LFoo;)V")));
233 assertThat(new Signature("(LFoo;LBar;)V"), is(new Signature("(LFoo;LBar;)V")));
234 assertThat(new Signature("([I)V"), is(new Signature("([I)V")));
235 assertThat(new Signature("([[D[[[J)V"), is(new Signature("([[D[[[J)V")));
236
237 assertThat(new Signature("()V"), is(not(new Signature("(I)V"))));
238 assertThat(new Signature("(I)V"), is(not(new Signature("()V"))));
239 assertThat(new Signature("(IJ)V"), is(not(new Signature("(JI)V"))));
240 assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V"))));
241 assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V"))));
242 assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V"))));
243
244 // return type
245 assertThat(new Signature("()I"), is(new Signature("()I")));
246 assertThat(new Signature("()Z"), is(new Signature("()Z")));
247 assertThat(new Signature("()[D"), is(new Signature("()[D")));
248 assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z")));
249 assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;")));
250 assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;")));
251
252 assertThat(new Signature("()I"), is(not(new Signature("()Z"))));
253 assertThat(new Signature("()Z"), is(not(new Signature("()I"))));
254 assertThat(new Signature("()[D"), is(not(new Signature("()[J"))));
255 assertThat(new Signature("()[[[Z"), is(not(new Signature("()[[Z"))));
256 assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;"))));
257 assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;"))));
258 }
259
260 @Test
261 public void testToString() {
262 assertThat(new Signature("()V").toString(), is("()V"));
263 assertThat(new Signature("(I)V").toString(), is("(I)V"));
264 assertThat(new Signature("(ZIZ)V").toString(), is("(ZIZ)V"));
265 assertThat(new Signature("(LFoo;)V").toString(), is("(LFoo;)V"));
266 assertThat(new Signature("(LFoo;LBar;)V").toString(), is("(LFoo;LBar;)V"));
267 assertThat(new Signature("([I)V").toString(), is("([I)V"));
268 assertThat(new Signature("([[D[[[J)V").toString(), is("([[D[[[J)V"));
269 }
270}
diff --git a/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
index e40d5fdc..0148f2c3 100644
--- a/src/test/java/cuchaz/enigma/TestTokensConstructors.java
+++ b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
@@ -11,131 +11,127 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.BehaviorEntry; 14import cuchaz.enigma.mapping.MethodEntry;
15import org.junit.Test; 15import org.junit.Test;
16 16
17import java.util.jar.JarFile; 17import java.util.jar.JarFile;
18 18
19import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor;
20import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; 19import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod;
21import static cuchaz.enigma.TestEntryFactory.newConstructor; 20import static cuchaz.enigma.TestEntryFactory.newMethod;
22import static org.hamcrest.MatcherAssert.assertThat; 21import static org.hamcrest.MatcherAssert.assertThat;
23import static org.hamcrest.Matchers.containsInAnyOrder; 22import static org.hamcrest.Matchers.*;
24import static org.hamcrest.Matchers.empty;
25import static org.hamcrest.Matchers.is;
26import static org.hamcrest.Matchers.nullValue;
27 23
28public class TestTokensConstructors extends TokenChecker { 24public class TestTokensConstructors extends TokenChecker {
29 25
30 public TestTokensConstructors() 26 public TestTokensConstructors()
31 throws Exception { 27 throws Exception {
32 super(new JarFile("build/test-obf/constructors.jar")); 28 super(new JarFile("build/test-obf/constructors.jar"));
33 } 29 }
34 30
35 @Test 31 @Test
36 public void baseDeclarations() { 32 public void baseDeclarations() {
37 assertThat(getDeclarationToken(newConstructor("a", "()V")), is("a")); 33 assertThat(getDeclarationToken(newMethod("a", "<init>", "()V")), is("a"));
38 assertThat(getDeclarationToken(newConstructor("a", "(I)V")), is("a")); 34 assertThat(getDeclarationToken(newMethod("a", "<init>", "(I)V")), is("a"));
39 } 35 }
40 36
41 @Test 37 @Test
42 public void subDeclarations() { 38 public void subDeclarations() {
43 assertThat(getDeclarationToken(newConstructor("d", "()V")), is("d")); 39 assertThat(getDeclarationToken(newMethod("d", "<init>", "()V")), is("d"));
44 assertThat(getDeclarationToken(newConstructor("d", "(I)V")), is("d")); 40 assertThat(getDeclarationToken(newMethod("d", "<init>", "(I)V")), is("d"));
45 assertThat(getDeclarationToken(newConstructor("d", "(II)V")), is("d")); 41 assertThat(getDeclarationToken(newMethod("d", "<init>", "(II)V")), is("d"));
46 assertThat(getDeclarationToken(newConstructor("d", "(III)V")), is("d")); 42 assertThat(getDeclarationToken(newMethod("d", "<init>", "(III)V")), is("d"));
47 } 43 }
48 44
49 @Test 45 @Test
50 public void subsubDeclarations() { 46 public void subsubDeclarations() {
51 assertThat(getDeclarationToken(newConstructor("e", "(I)V")), is("e")); 47 assertThat(getDeclarationToken(newMethod("e", "<init>", "(I)V")), is("e"));
52 } 48 }
53 49
54 @Test 50 @Test
55 public void defaultDeclarations() { 51 public void defaultDeclarations() {
56 assertThat(getDeclarationToken(newConstructor("c", "()V")), nullValue()); 52 assertThat(getDeclarationToken(newMethod("c", "<init>", "()V")), nullValue());
57 } 53 }
58 54
59 @Test 55 @Test
60 public void baseDefaultReferences() { 56 public void baseDefaultReferences() {
61 BehaviorEntry source = newConstructor("a", "()V"); 57 MethodEntry source = newMethod("a", "<init>", "()V");
62 assertThat( 58 assertThat(
63 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")), 59 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")),
64 containsInAnyOrder("a") 60 containsInAnyOrder("a")
65 ); 61 );
66 assertThat( 62 assertThat(
67 getReferenceTokens(newBehaviorReferenceByConstructor(source, "d", "()V")), 63 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "()V")),
68 is(empty()) // implicit call, not decompiled to token 64 is(empty()) // implicit call, not decompiled to token
69 ); 65 );
70 assertThat( 66 assertThat(
71 getReferenceTokens(newBehaviorReferenceByConstructor(source, "d", "(III)V")), 67 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(III)V")),
72 is(empty()) // implicit call, not decompiled to token 68 is(empty()) // implicit call, not decompiled to token
73 ); 69 );
74 } 70 }
75 71
76 @Test 72 @Test
77 public void baseIntReferences() { 73 public void baseIntReferences() {
78 BehaviorEntry source = newConstructor("a", "(I)V"); 74 MethodEntry source = newMethod("a", "<init>", "(I)V");
79 assertThat( 75 assertThat(
80 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")), 76 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")),
81 containsInAnyOrder("a") 77 containsInAnyOrder("a")
82 ); 78 );
83 } 79 }
84 80
85 @Test 81 @Test
86 public void subDefaultReferences() { 82 public void subDefaultReferences() {
87 BehaviorEntry source = newConstructor("d", "()V"); 83 MethodEntry source = newMethod("d", "<init>", "()V");
88 assertThat( 84 assertThat(
89 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")), 85 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")),
90 containsInAnyOrder("d") 86 containsInAnyOrder("d")
91 ); 87 );
92 assertThat( 88 assertThat(
93 getReferenceTokens(newBehaviorReferenceByConstructor(source, "d", "(I)V")), 89 getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "<init>", "(I)V")),
94 containsInAnyOrder("this") 90 containsInAnyOrder("this")
95 ); 91 );
96 } 92 }
97 93
98 @Test 94 @Test
99 public void subIntReferences() { 95 public void subIntReferences() {
100 BehaviorEntry source = newConstructor("d", "(I)V"); 96 MethodEntry source = newMethod("d", "<init>", "(I)V");
101 assertThat(getReferenceTokens( 97 assertThat(getReferenceTokens(
102 newBehaviorReferenceByMethod(source, "b", "d", "()V")), 98 newBehaviorReferenceByMethod(source, "b", "d", "()V")),
103 containsInAnyOrder("d") 99 containsInAnyOrder("d")
104 ); 100 );
105 assertThat(getReferenceTokens( 101 assertThat(getReferenceTokens(
106 newBehaviorReferenceByConstructor(source, "d", "(II)V")), 102 newBehaviorReferenceByMethod(source, "d", "<init>", "(II)V")),
107 containsInAnyOrder("this") 103 containsInAnyOrder("this")
108 ); 104 );
109 assertThat(getReferenceTokens( 105 assertThat(getReferenceTokens(
110 newBehaviorReferenceByConstructor(source, "e", "(I)V")), 106 newBehaviorReferenceByMethod(source, "e", "<init>", "(I)V")),
111 containsInAnyOrder("super") 107 containsInAnyOrder("super")
112 ); 108 );
113 } 109 }
114 110
115 @Test 111 @Test
116 public void subIntIntReferences() { 112 public void subIntIntReferences() {
117 BehaviorEntry source = newConstructor("d", "(II)V"); 113 MethodEntry source = newMethod("d", "<init>", "(II)V");
118 assertThat( 114 assertThat(
119 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")), 115 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")),
120 containsInAnyOrder("d") 116 containsInAnyOrder("d")
121 ); 117 );
122 } 118 }
123 119
124 @Test 120 @Test
125 public void subsubIntReferences() { 121 public void subsubIntReferences() {
126 BehaviorEntry source = newConstructor("e", "(I)V"); 122 MethodEntry source = newMethod("e", "<init>", "(I)V");
127 assertThat( 123 assertThat(
128 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")), 124 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")),
129 containsInAnyOrder("e") 125 containsInAnyOrder("e")
130 ); 126 );
131 } 127 }
132 128
133 @Test 129 @Test
134 public void defaultConstructableReferences() { 130 public void defaultConstructableReferences() {
135 BehaviorEntry source = newConstructor("c", "()V"); 131 MethodEntry source = newMethod("c", "<init>", "()V");
136 assertThat( 132 assertThat(
137 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")), 133 getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")),
138 containsInAnyOrder("c") 134 containsInAnyOrder("c")
139 ); 135 );
140 } 136 }
141} 137}
diff --git a/src/test/java/cuchaz/enigma/TestTypeDescriptor.java b/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
new file mode 100644
index 00000000..b874f629
--- /dev/null
+++ b/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
@@ -0,0 +1,243 @@
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 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma;
13
14import cuchaz.enigma.mapping.TypeDescriptor;
15import org.junit.Test;
16
17import static cuchaz.enigma.TestEntryFactory.newClass;
18import static org.hamcrest.MatcherAssert.assertThat;
19import static org.hamcrest.Matchers.is;
20import static org.hamcrest.Matchers.not;
21
22public class TestTypeDescriptor {
23
24 @Test
25 public void isVoid() {
26 assertThat(new TypeDescriptor("V").isVoid(), is(true));
27 assertThat(new TypeDescriptor("Z").isVoid(), is(false));
28 assertThat(new TypeDescriptor("B").isVoid(), is(false));
29 assertThat(new TypeDescriptor("C").isVoid(), is(false));
30 assertThat(new TypeDescriptor("I").isVoid(), is(false));
31 assertThat(new TypeDescriptor("J").isVoid(), is(false));
32 assertThat(new TypeDescriptor("F").isVoid(), is(false));
33 assertThat(new TypeDescriptor("D").isVoid(), is(false));
34 assertThat(new TypeDescriptor("LFoo;").isVoid(), is(false));
35 assertThat(new TypeDescriptor("[I").isVoid(), is(false));
36 }
37
38 @Test
39 public void isPrimitive() {
40 assertThat(new TypeDescriptor("V").isPrimitive(), is(false));
41 assertThat(new TypeDescriptor("Z").isPrimitive(), is(true));
42 assertThat(new TypeDescriptor("B").isPrimitive(), is(true));
43 assertThat(new TypeDescriptor("C").isPrimitive(), is(true));
44 assertThat(new TypeDescriptor("I").isPrimitive(), is(true));
45 assertThat(new TypeDescriptor("J").isPrimitive(), is(true));
46 assertThat(new TypeDescriptor("F").isPrimitive(), is(true));
47 assertThat(new TypeDescriptor("D").isPrimitive(), is(true));
48 assertThat(new TypeDescriptor("LFoo;").isPrimitive(), is(false));
49 assertThat(new TypeDescriptor("[I").isPrimitive(), is(false));
50 }
51
52 @Test
53 public void getPrimitive() {
54 assertThat(new TypeDescriptor("Z").getPrimitive(), is(TypeDescriptor.Primitive.Boolean));
55 assertThat(new TypeDescriptor("B").getPrimitive(), is(TypeDescriptor.Primitive.Byte));
56 assertThat(new TypeDescriptor("C").getPrimitive(), is(TypeDescriptor.Primitive.Character));
57 assertThat(new TypeDescriptor("I").getPrimitive(), is(TypeDescriptor.Primitive.Integer));
58 assertThat(new TypeDescriptor("J").getPrimitive(), is(TypeDescriptor.Primitive.Long));
59 assertThat(new TypeDescriptor("F").getPrimitive(), is(TypeDescriptor.Primitive.Float));
60 assertThat(new TypeDescriptor("D").getPrimitive(), is(TypeDescriptor.Primitive.Double));
61 }
62
63 @Test
64 public void isClass() {
65 assertThat(new TypeDescriptor("V").isType(), is(false));
66 assertThat(new TypeDescriptor("Z").isType(), is(false));
67 assertThat(new TypeDescriptor("B").isType(), is(false));
68 assertThat(new TypeDescriptor("C").isType(), is(false));
69 assertThat(new TypeDescriptor("I").isType(), is(false));
70 assertThat(new TypeDescriptor("J").isType(), is(false));
71 assertThat(new TypeDescriptor("F").isType(), is(false));
72 assertThat(new TypeDescriptor("D").isType(), is(false));
73 assertThat(new TypeDescriptor("LFoo;").isType(), is(true));
74 assertThat(new TypeDescriptor("[I").isType(), is(false));
75 }
76
77 @Test
78 public void getClassEntry() {
79 assertThat(new TypeDescriptor("LFoo;").getOwnerEntry(), is(newClass("Foo")));
80 assertThat(new TypeDescriptor("Ljava/lang/String;").getOwnerEntry(), is(newClass("java/lang/String")));
81 }
82
83 @Test
84 public void getArrayClassEntry() {
85 assertThat(new TypeDescriptor("[LFoo;").getOwnerEntry(), is(newClass("Foo")));
86 assertThat(new TypeDescriptor("[[[Ljava/lang/String;").getOwnerEntry(), is(newClass("java/lang/String")));
87 }
88
89 @Test
90 public void isArray() {
91 assertThat(new TypeDescriptor("V").isArray(), is(false));
92 assertThat(new TypeDescriptor("Z").isArray(), is(false));
93 assertThat(new TypeDescriptor("B").isArray(), is(false));
94 assertThat(new TypeDescriptor("C").isArray(), is(false));
95 assertThat(new TypeDescriptor("I").isArray(), is(false));
96 assertThat(new TypeDescriptor("J").isArray(), is(false));
97 assertThat(new TypeDescriptor("F").isArray(), is(false));
98 assertThat(new TypeDescriptor("D").isArray(), is(false));
99 assertThat(new TypeDescriptor("LFoo;").isArray(), is(false));
100 assertThat(new TypeDescriptor("[I").isArray(), is(true));
101 }
102
103 @Test
104 public void getArrayDimension() {
105 assertThat(new TypeDescriptor("[I").getArrayDimension(), is(1));
106 assertThat(new TypeDescriptor("[[I").getArrayDimension(), is(2));
107 assertThat(new TypeDescriptor("[[[I").getArrayDimension(), is(3));
108 }
109
110 @Test
111 public void getArrayType() {
112 assertThat(new TypeDescriptor("[I").getArrayType(), is(new TypeDescriptor("I")));
113 assertThat(new TypeDescriptor("[[I").getArrayType(), is(new TypeDescriptor("I")));
114 assertThat(new TypeDescriptor("[[[I").getArrayType(), is(new TypeDescriptor("I")));
115 assertThat(new TypeDescriptor("[Ljava/lang/String;").getArrayType(), is(new TypeDescriptor("Ljava/lang/String;")));
116 }
117
118 @Test
119 public void hasClass() {
120 assertThat(new TypeDescriptor("LFoo;").containsType(), is(true));
121 assertThat(new TypeDescriptor("Ljava/lang/String;").containsType(), is(true));
122 assertThat(new TypeDescriptor("[LBar;").containsType(), is(true));
123 assertThat(new TypeDescriptor("[[[LCat;").containsType(), is(true));
124
125 assertThat(new TypeDescriptor("V").containsType(), is(false));
126 assertThat(new TypeDescriptor("[I").containsType(), is(false));
127 assertThat(new TypeDescriptor("[[[I").containsType(), is(false));
128 assertThat(new TypeDescriptor("Z").containsType(), is(false));
129 }
130
131 @Test
132 public void parseVoid() {
133 final String answer = "V";
134 assertThat(TypeDescriptor.parseFirst("V"), is(answer));
135 assertThat(TypeDescriptor.parseFirst("VVV"), is(answer));
136 assertThat(TypeDescriptor.parseFirst("VIJ"), is(answer));
137 assertThat(TypeDescriptor.parseFirst("V[I"), is(answer));
138 assertThat(TypeDescriptor.parseFirst("VLFoo;"), is(answer));
139 assertThat(TypeDescriptor.parseFirst("V[LFoo;"), is(answer));
140 }
141
142 @Test
143 public void parsePrimitive() {
144 final String answer = "I";
145 assertThat(TypeDescriptor.parseFirst("I"), is(answer));
146 assertThat(TypeDescriptor.parseFirst("III"), is(answer));
147 assertThat(TypeDescriptor.parseFirst("IJZ"), is(answer));
148 assertThat(TypeDescriptor.parseFirst("I[I"), is(answer));
149 assertThat(TypeDescriptor.parseFirst("ILFoo;"), is(answer));
150 assertThat(TypeDescriptor.parseFirst("I[LFoo;"), is(answer));
151 }
152
153 @Test
154 public void parseClass() {
155 {
156 final String answer = "LFoo;";
157 assertThat(TypeDescriptor.parseFirst("LFoo;"), is(answer));
158 assertThat(TypeDescriptor.parseFirst("LFoo;I"), is(answer));
159 assertThat(TypeDescriptor.parseFirst("LFoo;JZ"), is(answer));
160 assertThat(TypeDescriptor.parseFirst("LFoo;[I"), is(answer));
161 assertThat(TypeDescriptor.parseFirst("LFoo;LFoo;"), is(answer));
162 assertThat(TypeDescriptor.parseFirst("LFoo;[LFoo;"), is(answer));
163 }
164 {
165 final String answer = "Ljava/lang/String;";
166 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;"), is(answer));
167 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;I"), is(answer));
168 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;JZ"), is(answer));
169 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;[I"), is(answer));
170 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;LFoo;"), is(answer));
171 assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;[LFoo;"), is(answer));
172 }
173 }
174
175 @Test
176 public void parseArray() {
177 {
178 final String answer = "[I";
179 assertThat(TypeDescriptor.parseFirst("[I"), is(answer));
180 assertThat(TypeDescriptor.parseFirst("[III"), is(answer));
181 assertThat(TypeDescriptor.parseFirst("[IJZ"), is(answer));
182 assertThat(TypeDescriptor.parseFirst("[I[I"), is(answer));
183 assertThat(TypeDescriptor.parseFirst("[ILFoo;"), is(answer));
184 }
185 {
186 final String answer = "[[I";
187 assertThat(TypeDescriptor.parseFirst("[[I"), is(answer));
188 assertThat(TypeDescriptor.parseFirst("[[III"), is(answer));
189 assertThat(TypeDescriptor.parseFirst("[[IJZ"), is(answer));
190 assertThat(TypeDescriptor.parseFirst("[[I[I"), is(answer));
191 assertThat(TypeDescriptor.parseFirst("[[ILFoo;"), is(answer));
192 }
193 {
194 final String answer = "[LFoo;";
195 assertThat(TypeDescriptor.parseFirst("[LFoo;"), is(answer));
196 assertThat(TypeDescriptor.parseFirst("[LFoo;II"), is(answer));
197 assertThat(TypeDescriptor.parseFirst("[LFoo;JZ"), is(answer));
198 assertThat(TypeDescriptor.parseFirst("[LFoo;[I"), is(answer));
199 assertThat(TypeDescriptor.parseFirst("[LFoo;LFoo;"), is(answer));
200 }
201 }
202
203 @Test
204 public void equals() {
205 assertThat(new TypeDescriptor("V"), is(new TypeDescriptor("V")));
206 assertThat(new TypeDescriptor("Z"), is(new TypeDescriptor("Z")));
207 assertThat(new TypeDescriptor("B"), is(new TypeDescriptor("B")));
208 assertThat(new TypeDescriptor("C"), is(new TypeDescriptor("C")));
209 assertThat(new TypeDescriptor("I"), is(new TypeDescriptor("I")));
210 assertThat(new TypeDescriptor("J"), is(new TypeDescriptor("J")));
211 assertThat(new TypeDescriptor("F"), is(new TypeDescriptor("F")));
212 assertThat(new TypeDescriptor("D"), is(new TypeDescriptor("D")));
213 assertThat(new TypeDescriptor("LFoo;"), is(new TypeDescriptor("LFoo;")));
214 assertThat(new TypeDescriptor("[I"), is(new TypeDescriptor("[I")));
215 assertThat(new TypeDescriptor("[[[I"), is(new TypeDescriptor("[[[I")));
216 assertThat(new TypeDescriptor("[LFoo;"), is(new TypeDescriptor("[LFoo;")));
217
218 assertThat(new TypeDescriptor("V"), is(not(new TypeDescriptor("I"))));
219 assertThat(new TypeDescriptor("I"), is(not(new TypeDescriptor("J"))));
220 assertThat(new TypeDescriptor("I"), is(not(new TypeDescriptor("LBar;"))));
221 assertThat(new TypeDescriptor("I"), is(not(new TypeDescriptor("[I"))));
222 assertThat(new TypeDescriptor("LFoo;"), is(not(new TypeDescriptor("LBar;"))));
223 assertThat(new TypeDescriptor("[I"), is(not(new TypeDescriptor("[Z"))));
224 assertThat(new TypeDescriptor("[[[I"), is(not(new TypeDescriptor("[I"))));
225 assertThat(new TypeDescriptor("[LFoo;"), is(not(new TypeDescriptor("[LBar;"))));
226 }
227
228 @Test
229 public void testToString() {
230 assertThat(new TypeDescriptor("V").toString(), is("V"));
231 assertThat(new TypeDescriptor("Z").toString(), is("Z"));
232 assertThat(new TypeDescriptor("B").toString(), is("B"));
233 assertThat(new TypeDescriptor("C").toString(), is("C"));
234 assertThat(new TypeDescriptor("I").toString(), is("I"));
235 assertThat(new TypeDescriptor("J").toString(), is("J"));
236 assertThat(new TypeDescriptor("F").toString(), is("F"));
237 assertThat(new TypeDescriptor("D").toString(), is("D"));
238 assertThat(new TypeDescriptor("LFoo;").toString(), is("LFoo;"));
239 assertThat(new TypeDescriptor("[I").toString(), is("[I"));
240 assertThat(new TypeDescriptor("[[[I").toString(), is("[[[I"));
241 assertThat(new TypeDescriptor("[LFoo;").toString(), is("[LFoo;"));
242 }
243}