summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Modmuss502018-07-18 13:46:00 +0100
committerGravatar GitHub2018-07-18 13:46:00 +0100
commit1ebe691c12f68beea378b133ddc4bcbde7f3f795 (patch)
treefb051d9fde5644bd144a7e9d7bcecc70a256359c
parentRecursively rebuild method names (diff)
parentUpdate version number (diff)
downloadenigma-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.gz
enigma-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.xz
enigma-1ebe691c12f68beea378b133ddc4bcbde7f3f795.zip
Merge pull request #62 from OpenModLoader/asm
ASM based class translator
-rw-r--r--.gitignore1
-rw-r--r--build.gradle11
-rw-r--r--src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java20
-rw-r--r--src/main/java/cuchaz/enigma/CachingTypeLoader.java38
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java2
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java283
-rw-r--r--src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java19
-rw-r--r--src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java39
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java187
-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.java12
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java16
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java18
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java63
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java29
-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.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarClassIterator.java123
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java635
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java13
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java16
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java (renamed from src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java)41
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java95
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java44
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java)154
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java122
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/AccessFlags.java80
-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.java85
-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/TranslationAnnotationVisitor.java43
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java113
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java33
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java191
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java129
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java8
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java6
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java67
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java24
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiTricks.java5
-rw-r--r--src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java2
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java110
-rw-r--r--src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java210
-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.java370
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java132
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java87
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java38
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java102
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java (renamed from src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java)15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java58
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java14
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java167
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java124
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java164
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java3
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java114
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java90
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java139
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java67
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java128
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java365
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java (renamed from src/main/java/cuchaz/enigma/mapping/Type.java)117
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java37
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java (renamed from src/main/java/cuchaz/enigma/mapping/ClassEntry.java)32
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/Entry.java (renamed from src/main/java/cuchaz/enigma/mapping/Entry.java)6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java49
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java43
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java77
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java57
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java82
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java54
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java80
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java48
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java53
-rw-r--r--src/main/java/cuchaz/enigma/throwables/MappingParseException.java7
-rw-r--r--src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java414
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfed.java8
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfuscator.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestEntryFactory.java31
-rw-r--r--src/test/java/cuchaz/enigma/TestInnerClasses.java49
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java80
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java111
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java38
-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/TestSourceIndex.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestTokensConstructors.java90
-rw-r--r--src/test/java/cuchaz/enigma/TestTranslator.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestType.java243
-rw-r--r--src/test/java/cuchaz/enigma/TestTypeDescriptor.java243
-rw-r--r--src/test/java/cuchaz/enigma/TokenChecker.java2
122 files changed, 4611 insertions, 5929 deletions
diff --git a/.gitignore b/.gitignore
index 1729e4d6..662e408b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -150,6 +150,7 @@ flycheck_*.el
150 150
151# IntelliJ 151# IntelliJ
152/out/ 152/out/
153.idea
153 154
154# mpeltonen/sbt-idea plugin 155# mpeltonen/sbt-idea plugin
155.idea_modules/ 156.idea_modules/
diff --git a/build.gradle b/build.gradle
index c65ccca2..5a0de102 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ apply plugin: 'com.github.johnrengelman.shadow'
19apply plugin: 'maven' 19apply plugin: 'maven'
20 20
21group = 'cuchaz' 21group = 'cuchaz'
22version = '0.11.0' 22version = '0.12.0'
23 23
24def ENV = System.getenv() 24def ENV = System.getenv()
25if (ENV.BUILD_NUMBER) { 25if (ENV.BUILD_NUMBER) {
@@ -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.22.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
@@ -159,9 +158,15 @@ task('libJar', type: Jar, dependsOn: classes) {
159 } 158 }
160} 159}
161 160
161task sourcesJar(type: Jar, dependsOn: classes) {
162 classifier = 'sources'
163 from sourceSets.main.allSource
164}
165
162artifacts { 166artifacts {
163 archives shadowJar 167 archives shadowJar
164 archives libJar 168 archives libJar
169 archives sourcesJar
165} 170}
166 171
167// And finally, make the build generate / install the jars. 172// And finally, make the build generate / install the jars.
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
new file mode 100644
index 00000000..58682e23
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
@@ -0,0 +1,20 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ClasspathTypeLoader;
5import com.strobel.assembler.metadata.ITypeLoader;
6
7/**
8 * Caching version of {@link ClasspathTypeLoader}
9 */
10public class CachingClasspathTypeLoader extends CachingTypeLoader {
11 private final ITypeLoader classpathLoader = new ClasspathTypeLoader();
12
13 protected byte[] doLoad(String className) {
14 Buffer parentBuf = new Buffer();
15 if (classpathLoader.tryLoadType(className, parentBuf)) {
16 return parentBuf.array();
17 }
18 return EMPTY_ARRAY;//need to return *something* as null means no store
19 }
20}
diff --git a/src/main/java/cuchaz/enigma/CachingTypeLoader.java b/src/main/java/cuchaz/enigma/CachingTypeLoader.java
new file mode 100644
index 00000000..22c31c63
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/CachingTypeLoader.java
@@ -0,0 +1,38 @@
1package cuchaz.enigma;
2
3import com.google.common.collect.Maps;
4import com.strobel.assembler.metadata.Buffer;
5import com.strobel.assembler.metadata.ITypeLoader;
6
7import java.util.Map;
8
9/**
10 * Common cache functions
11 */
12public abstract class CachingTypeLoader implements ITypeLoader {
13 protected static final byte[] EMPTY_ARRAY = {};
14
15 private final Map<String, byte[]> cache = Maps.newHashMap();
16
17 protected abstract byte[] doLoad(String className);
18
19 @Override
20 public boolean tryLoadType(String className, Buffer out) {
21
22 // check the cache
23 byte[] data = this.cache.computeIfAbsent(className, this::doLoad);
24
25 if (data == EMPTY_ARRAY) {
26 return false;
27 }
28
29 out.reset(data.length);
30 System.arraycopy(data, 0, out.array(), out.position(), data.length);
31 out.position(0);
32 return true;
33 }
34
35 public void clearCache() {
36 this.cache.clear();
37 }
38}
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 6ef020c7..6ea1c40b 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.base.Charsets; 14import com.google.common.base.Stopwatch;
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.Sets; 17import com.google.common.collect.Sets;
@@ -25,60 +25,66 @@ import com.strobel.decompiler.languages.java.JavaOutputVisitor;
25import com.strobel.decompiler.languages.java.ast.AstBuilder; 25import com.strobel.decompiler.languages.java.ast.AstBuilder;
26import com.strobel.decompiler.languages.java.ast.CompilationUnit; 26import com.strobel.decompiler.languages.java.ast.CompilationUnit;
27import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 27import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
28import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
28import cuchaz.enigma.analysis.*; 29import cuchaz.enigma.analysis.*;
29import cuchaz.enigma.bytecode.ClassProtectifier; 30import cuchaz.enigma.bytecode.ClassProtectifier;
30import cuchaz.enigma.bytecode.ClassPublifier; 31import cuchaz.enigma.bytecode.ClassPublifier;
31import cuchaz.enigma.mapping.*; 32import cuchaz.enigma.mapping.*;
33import cuchaz.enigma.mapping.entry.*;
32import cuchaz.enigma.throwables.IllegalNameException; 34import cuchaz.enigma.throwables.IllegalNameException;
33import cuchaz.enigma.utils.Utils; 35import cuchaz.enigma.utils.Utils;
34import javassist.CtClass; 36import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform;
35import javassist.bytecode.Descriptor; 37import org.objectweb.asm.ClassWriter;
38import org.objectweb.asm.Opcodes;
39import org.objectweb.asm.tree.ClassNode;
36 40
37import java.io.*; 41import java.io.*;
38import java.util.*; 42import java.util.*;
43import java.util.concurrent.atomic.AtomicInteger;
39import java.util.jar.JarEntry; 44import java.util.jar.JarEntry;
40import java.util.jar.JarFile; 45import java.util.jar.JarFile;
41import java.util.jar.JarOutputStream; 46import java.util.jar.JarOutputStream;
42 47
43public class Deobfuscator { 48public class Deobfuscator {
44 49
45 private final JarFile jar; 50 private final ReferencedEntryPool entryPool = new ReferencedEntryPool();
51 private final ParsedJar parsedJar;
46 private final DecompilerSettings settings; 52 private final DecompilerSettings settings;
47 private final JarIndex jarIndex; 53 private final JarIndex jarIndex;
48 private final MappingsRenamer renamer; 54 private final MappingsRenamer renamer;
49 private final Map<TranslationDirection, Translator> translatorCache; 55 private final Map<TranslationDirection, Translator> translatorCache;
50 private Mappings mappings; 56 private Mappings mappings;
51 57
52 public Deobfuscator(JarFile jar) { 58 public Deobfuscator(ParsedJar jar) {
53 this.jar = jar; 59 this.parsedJar = jar;
54 60
55 // build the jar index 61 // build the jar index
56 this.jarIndex = new JarIndex(); 62 this.jarIndex = new JarIndex(entryPool);
57 this.jarIndex.indexJar(this.jar, true); 63 this.jarIndex.indexJar(this.parsedJar, true);
58 64
59 // config the decompiler 65 // config the decompiler
60 this.settings = DecompilerSettings.javaDefaults(); 66 this.settings = DecompilerSettings.javaDefaults();
61 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 67 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
62 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); 68 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
63 this.settings.setForceExplicitTypeArguments( 69 this.settings.setForceExplicitTypeArguments(
64 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); 70 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
65 // DEBUG 71 // DEBUG
66 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 72 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
67 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 73 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
68 74
69 // init defaults 75 // init defaults
70 this.translatorCache = Maps.newTreeMap(); 76 this.translatorCache = Maps.newTreeMap();
71 this.renamer = new MappingsRenamer(this.jarIndex, null); 77 this.renamer = new MappingsRenamer(this.jarIndex, null, this.entryPool);
72 // init mappings 78 // init mappings
73 setMappings(new Mappings()); 79 setMappings(new Mappings());
74 } 80 }
75 81
76 public JarFile getJar() { 82 public Deobfuscator(JarFile jar) throws IOException {
77 return this.jar; 83 this(new ParsedJar(jar));
78 } 84 }
79 85
80 public String getJarName() { 86 public ParsedJar getJar() {
81 return this.jar.getName(); 87 return this.parsedJar;
82 } 88 }
83 89
84 public JarIndex getJarIndex() { 90 public JarIndex getJarIndex() {
@@ -102,16 +108,16 @@ public class Deobfuscator {
102 MappingsChecker checker = new MappingsChecker(this.jarIndex); 108 MappingsChecker checker = new MappingsChecker(this.jarIndex);
103 checker.dropBrokenMappings(val); 109 checker.dropBrokenMappings(val);
104 if (warnAboutDrops) { 110 if (warnAboutDrops) {
105 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { 111 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."); 112 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
107 } 113 }
108 for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { 114 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."); 115 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
110 } 116 }
111 for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { 117 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."); 118 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
113 } 119 }
114 for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { 120 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."); 121 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
116 } 122 }
117 } 123 }
@@ -123,7 +129,7 @@ public class Deobfuscator {
123 129
124 public Translator getTranslator(TranslationDirection direction) { 130 public Translator getTranslator(TranslationDirection direction) {
125 return this.translatorCache.computeIfAbsent(direction, 131 return this.translatorCache.computeIfAbsent(direction,
126 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 132 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex()));
127 } 133 }
128 134
129 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 135 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
@@ -150,14 +156,19 @@ public class Deobfuscator {
150 156
151 public TranslatingTypeLoader createTypeLoader() { 157 public TranslatingTypeLoader createTypeLoader() {
152 return new TranslatingTypeLoader( 158 return new TranslatingTypeLoader(
153 this.jar, 159 this.parsedJar,
154 this.jarIndex, 160 this.jarIndex,
155 getTranslator(TranslationDirection.Obfuscating), 161 this.entryPool,
156 getTranslator(TranslationDirection.Deobfuscating) 162 getTranslator(TranslationDirection.OBFUSCATING),
163 getTranslator(TranslationDirection.DEOBFUSCATING)
157 ); 164 );
158 } 165 }
159 166
160 public CompilationUnit getSourceTree(String className) { 167 public CompilationUnit getSourceTree(String className) {
168 return getSourceTree(className, createTypeLoader());
169 }
170
171 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) {
161 172
162 // we don't know if this class name is obfuscated or deobfuscated 173 // we don't know if this class name is obfuscated or deobfuscated
163 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 174 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
@@ -172,15 +183,14 @@ public class Deobfuscator {
172 deobfClassName = classMapping.getDeobfName(); 183 deobfClassName = classMapping.getDeobfName();
173 } 184 }
174 185
175 // set the type loader 186 // set the desc loader
176 TranslatingTypeLoader loader = createTypeLoader();
177 this.settings.setTypeLoader(loader); 187 this.settings.setTypeLoader(loader);
178 188
179 // see if procyon can find the type 189 // see if procyon can find the desc
180 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 190 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
181 if (type == null) { 191 if (type == null) {
182 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", 192 throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s",
183 className, deobfClassName, loader.getClassNamesToTry(deobfClassName) 193 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
184 )); 194 ));
185 } 195 }
186 TypeDefinition resolvedType = type.resolve(); 196 TypeDefinition resolvedType = type.resolve();
@@ -192,6 +202,7 @@ public class Deobfuscator {
192 AstBuilder builder = new AstBuilder(context); 202 AstBuilder builder = new AstBuilder(context);
193 builder.addType(resolvedType); 203 builder.addType(resolvedType);
194 builder.runTransformations(null); 204 builder.runTransformations(null);
205 runCustomTransforms(builder, context);
195 return builder.getCompilationUnit(); 206 return builder.getCompilationUnit();
196 } 207 }
197 208
@@ -208,7 +219,7 @@ public class Deobfuscator {
208 } else { 219 } else {
209 index = new SourceIndex(source); 220 index = new SourceIndex(source);
210 } 221 }
211 sourceTree.acceptVisitor(new SourceIndexVisitor(), index); 222 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index);
212 223
213 // DEBUG 224 // DEBUG
214 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); 225 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
@@ -221,10 +232,10 @@ public class Deobfuscator {
221 Entry obfEntry = obfuscateEntry(deobfReference.entry); 232 Entry obfEntry = obfuscateEntry(deobfReference.entry);
222 233
223 // try to resolve the class 234 // try to resolve the class
224 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); 235 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryOwner(obfEntry);
225 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 236 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getOwnerClassEntry())) {
226 // change the class of the entry 237 // change the class of the entry
227 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 238 obfEntry = obfEntry.updateOwnership(resolvedObfClassEntry);
228 239
229 // save the new deobfuscated reference 240 // save the new deobfuscated reference
230 deobfReference.entry = deobfuscateEntry(obfEntry); 241 deobfReference.entry = deobfuscateEntry(obfEntry);
@@ -262,23 +273,29 @@ public class Deobfuscator {
262 progress.init(classEntries.size(), "Decompiling classes..."); 273 progress.init(classEntries.size(), "Decompiling classes...");
263 } 274 }
264 275
276 //create a common instance outside the loop as mappings shouldn't be changing while this is happening
277 //synchronized to make sure the parallelStream doesn't CME with the cache
278 ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader());
279
265 // DEOBFUSCATE ALL THE THINGS!! @_@ 280 // DEOBFUSCATE ALL THE THINGS!! @_@
266 int i = 0; 281 Stopwatch stopwatch = Stopwatch.createStarted();
267 for (ClassEntry obfClassEntry : classEntries) { 282 AtomicInteger count = new AtomicInteger();
283 classEntries.parallelStream().forEach(obfClassEntry -> {
268 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); 284 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry));
269 if (progress != null) { 285 if (progress != null) {
270 progress.onProgress(i++, deobfClassEntry.toString()); 286 progress.onProgress(count.getAndIncrement(), deobfClassEntry.toString());
271 } 287 }
272 288
273 try { 289 try {
274 // get the source 290 // get the source
275 String source = getSource(getSourceTree(obfClassEntry.getName())); 291 CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader);
276 292
277 // write the file 293 // write the file
278 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); 294 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java");
279 file.getParentFile().mkdirs(); 295 file.getParentFile().mkdirs();
280 try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { 296 try (Writer writer = new BufferedWriter(new FileWriter(file))) {
281 out.write(source); 297 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
298 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
282 } 299 }
283 } catch (Throwable t) { 300 } catch (Throwable t) {
284 // don't crash the whole world here, just log the error and keep going 301 // don't crash the whole world here, just log the error and keep going
@@ -286,9 +303,11 @@ public class Deobfuscator {
286 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")"); 303 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")");
287 t.printStackTrace(System.err); 304 t.printStackTrace(System.err);
288 } 305 }
289 } 306 });
307 stopwatch.stop();
308 System.out.println("writeSources Done in : " + stopwatch.toString());
290 if (progress != null) { 309 if (progress != null) {
291 progress.onProgress(i, "Done!"); 310 progress.onProgress(count.get(), "Done:");
292 } 311 }
293 } 312 }
294 313
@@ -305,18 +324,14 @@ public class Deobfuscator {
305 } 324 }
306 } 325 }
307 326
308 private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { 327 public boolean isMethodProvider(ClassEntry classObfEntry, MethodEntry methodEntry) {
309 if (behaviorEntry instanceof MethodEntry) { 328 Set<ClassEntry> classEntries = new HashSet<>();
310 MethodEntry methodEntry = (MethodEntry) behaviorEntry; 329 addAllPotentialAncestors(classEntries, classObfEntry);
311
312 Set<ClassEntry> classEntries = new HashSet<>();
313 addAllPotentialAncestors(classEntries, classObfEntry);
314 330
315 for (ClassEntry parentEntry : classEntries) { 331 for (ClassEntry parentEntry : classEntries) {
316 MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); 332 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
317 if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { 333 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
318 return false; 334 return false;
319 }
320 } 335 }
321 } 336 }
322 337
@@ -353,29 +368,29 @@ public class Deobfuscator {
353 368
354 try { 369 try {
355 rename(obfEntry, name); 370 rename(obfEntry, name);
356 } catch (IllegalNameException exception) 371 } catch (IllegalNameException exception) {
357 {
358 System.out.println("WARNING: " + exception.getMessage()); 372 System.out.println("WARNING: " + exception.getMessage());
359 } 373 }
360 } 374 }
361 } 375 }
362 } 376 }
363 377
364 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap){ 378 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) {
365 Map<Entry, String> renameEntries = new HashMap<>(); 379 Map<Entry, String> renameEntries = new HashMap<>();
366 380
367 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 381 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
368 ClassEntry classObfEntry = classMapping.getObfEntry(); 382 ClassEntry classObfEntry = classMapping.getObfEntry();
369 BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); 383 MethodEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
370 384
371 if (isBehaviorProvider(classObfEntry, obfEntry)) { 385 if (isMethodProvider(classObfEntry, obfEntry)) {
372 if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) 386 if (hasDeobfuscatedName(obfEntry)
373 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { 387 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
374 renameEntries.put(obfEntry, methodMapping.getDeobfName()); 388 renameEntries.put(obfEntry, methodMapping.getDeobfName());
375 } 389 }
376 390
377 for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { 391 ArrayList<LocalVariableMapping> arguments = Lists.newArrayList(methodMapping.arguments());
378 Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); 392 for (LocalVariableMapping localVariableMapping : arguments) {
393 Entry argObfEntry = localVariableMapping.getObfEntry(obfEntry);
379 if (hasDeobfuscatedName(argObfEntry)) { 394 if (hasDeobfuscatedName(argObfEntry)) {
380 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); 395 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
381 } 396 }
@@ -385,52 +400,56 @@ public class Deobfuscator {
385 400
386 classMapping.markDirty(); 401 classMapping.markDirty();
387 renameClassMap.put(classMapping, renameEntries); 402 renameClassMap.put(classMapping, renameEntries);
388 403 for (ClassMapping innerClass : classMapping.innerClasses()) {
389 for(ClassMapping innerClass : classMapping.innerClasses()){
390 rebuildMethodNames(innerClass, renameClassMap); 404 rebuildMethodNames(innerClass, renameClassMap);
391 } 405 }
392 } 406 }
393 407
394 408
395
396 public void writeJar(File out, ProgressListener progress) { 409 public void writeJar(File out, ProgressListener progress) {
397 transformJar(out, progress, createTypeLoader()::transformClass); 410 transformJar(out, progress, createTypeLoader()::transformInto);
398 } 411 }
399 412
400 public void protectifyJar(File out, ProgressListener progress) { 413 public void protectifyJar(File out, ProgressListener progress) {
401 transformJar(out, progress, ClassProtectifier::protectify); 414 transformJar(out, progress, (node, writer) -> {
415 node.accept(new ClassProtectifier(Opcodes.ASM5, writer));
416 return node.name;
417 });
402 } 418 }
403 419
404 public void publifyJar(File out, ProgressListener progress) { 420 public void publifyJar(File out, ProgressListener progress) {
405 transformJar(out, progress, ClassPublifier::publify); 421 transformJar(out, progress, (node, writer) -> {
422 node.accept(new ClassPublifier(Opcodes.ASM5, writer));
423 return node.name;
424 });
406 } 425 }
407 426
408 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 427 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
409 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 428 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
410 if (progress != null) { 429 if (progress != null) {
411 progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); 430 progress.init(parsedJar.getClassCount(), "Transforming classes...");
412 } 431 }
413 432
414 int i = 0; 433 AtomicInteger i = new AtomicInteger();
415 for (CtClass c : JarClassIterator.classes(this.jar)) { 434 parsedJar.visit(node -> {
416 if (progress != null) { 435 if (progress != null) {
417 progress.onProgress(i++, c.getName()); 436 progress.onProgress(i.getAndIncrement(), node.name);
418 } 437 }
419 438
420 try { 439 try {
421 c = transformer.transform(c); 440 ClassWriter writer = new ClassWriter(0);
422 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); 441 String transformedName = transformer.transform(node, writer);
423 outJar.write(c.toBytecode()); 442 outJar.putNextEntry(new JarEntry(transformedName.replace('.', '/') + ".class"));
443 outJar.write(writer.toByteArray());
424 outJar.closeEntry(); 444 outJar.closeEntry();
425 } catch (Throwable t) { 445 } catch (Throwable t) {
426 throw new Error("Unable to transform class " + c.getName(), t); 446 throw new Error("Unable to transform class " + node.name, t);
427 } 447 }
428 } 448 });
449
429 if (progress != null) { 450 if (progress != null) {
430 progress.onProgress(i, "Done!"); 451 progress.onProgress(i.get(), "Done!");
431 } 452 }
432
433 outJar.close();
434 } catch (IOException ex) { 453 } catch (IOException ex) {
435 throw new Error("Unable to write to Jar file!"); 454 throw new Error("Unable to write to Jar file!");
436 } 455 }
@@ -440,14 +459,22 @@ public class Deobfuscator {
440 if (deobfEntry == null) { 459 if (deobfEntry == null) {
441 return null; 460 return null;
442 } 461 }
443 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); 462 T translatedEntry = getTranslator(TranslationDirection.OBFUSCATING).getTranslatedEntry(deobfEntry);
463 if (translatedEntry == null) {
464 return deobfEntry;
465 }
466 return translatedEntry;
444 } 467 }
445 468
446 public <T extends Entry> T deobfuscateEntry(T obfEntry) { 469 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
447 if (obfEntry == null) { 470 if (obfEntry == null) {
448 return null; 471 return null;
449 } 472 }
450 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); 473 T translatedEntry = getTranslator(TranslationDirection.DEOBFUSCATING).getTranslatedEntry(obfEntry);
474 if (translatedEntry == null) {
475 return obfEntry;
476 }
477 return translatedEntry;
451 } 478 }
452 479
453 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { 480 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
@@ -475,7 +502,7 @@ public class Deobfuscator {
475 // HACKHACK: Object methods are not obfuscated identifiers 502 // HACKHACK: Object methods are not obfuscated identifiers
476 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 503 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
477 String name = obfMethodEntry.getName(); 504 String name = obfMethodEntry.getName();
478 String sig = obfMethodEntry.getSignature().toString(); 505 String sig = obfMethodEntry.getDesc().toString();
479 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { 506 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
480 return false; 507 return false;
481 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { 508 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
@@ -501,7 +528,7 @@ public class Deobfuscator {
501 } 528 }
502 529
503 // FIXME: HACK EVEN MORE HACK! 530 // FIXME: HACK EVEN MORE HACK!
504 if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) 531 if (hack && this.jarIndex.containsObfEntry(obfEntry.getOwnerClassEntry()))
505 return true; 532 return true;
506 } 533 }
507 534
@@ -517,27 +544,24 @@ public class Deobfuscator {
517 } 544 }
518 545
519 public boolean hasDeobfuscatedName(Entry obfEntry) { 546 public boolean hasDeobfuscatedName(Entry obfEntry) {
520 Translator translator = getTranslator(TranslationDirection.Deobfuscating); 547 Translator translator = getTranslator(TranslationDirection.DEOBFUSCATING);
521 if (obfEntry instanceof ClassEntry) { 548 if (obfEntry instanceof ClassEntry) {
522 ClassEntry obfClass = (ClassEntry) obfEntry; 549 ClassEntry obfClass = (ClassEntry) obfEntry;
523 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); 550 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
524 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); 551 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
525 return classMapping != null && classMapping.getDeobfName() != null; 552 return classMapping != null && classMapping.getDeobfName() != null;
526 } else if (obfEntry instanceof FieldEntry) { 553 } else if (obfEntry instanceof FieldEntry) {
527 return translator.translate((FieldEntry) obfEntry) != null; 554 return translator.hasFieldMapping((FieldEntry) obfEntry);
528 } else if (obfEntry instanceof MethodEntry) { 555 } else if (obfEntry instanceof MethodEntry) {
529 return translator.translate((MethodEntry) obfEntry) != null; 556 MethodEntry methodEntry = (MethodEntry) obfEntry;
530 } else if (obfEntry instanceof ConstructorEntry) { 557 if (methodEntry.isConstructor()) {
531 // constructors have no names 558 return false;
532 return false; 559 }
533 } else if (obfEntry instanceof ArgumentEntry) { 560 return translator.hasMethodMapping(methodEntry);
534 return translator.translate((ArgumentEntry) obfEntry) != null;
535 } else if (obfEntry instanceof LocalVariableEntry) { 561 } else if (obfEntry instanceof LocalVariableEntry) {
536 // TODO: Implement it 562 return translator.hasLocalVariableMapping((LocalVariableEntry) obfEntry);
537 //return translator.translate((LocalVariableEntry)obfEntry) != null;
538 return false;
539 } else { 563 } else {
540 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 564 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
541 } 565 }
542 } 566 }
543 567
@@ -549,19 +573,18 @@ public class Deobfuscator {
549 573
550 public void rename(Entry obfEntry, String newName, boolean clearCache) { 574 public void rename(Entry obfEntry, String newName, boolean clearCache) {
551 if (obfEntry instanceof ClassEntry) { 575 if (obfEntry instanceof ClassEntry) {
552 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 576 this.renamer.setClassName((ClassEntry) obfEntry, newName);
553 } else if (obfEntry instanceof FieldEntry) { 577 } else if (obfEntry instanceof FieldEntry) {
554 this.renamer.setFieldName((FieldEntry) obfEntry, newName); 578 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
555 } else if (obfEntry instanceof MethodEntry) { 579 } else if (obfEntry instanceof MethodEntry) {
580 if (((MethodEntry) obfEntry).isConstructor()) {
581 throw new IllegalArgumentException("Cannot rename constructors");
582 }
556 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); 583 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
557 } else if (obfEntry instanceof ConstructorEntry) {
558 throw new IllegalArgumentException("Cannot rename constructors");
559 } else if (obfEntry instanceof ArgumentEntry) {
560 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName);
561 } else if (obfEntry instanceof LocalVariableEntry) { 584 } else if (obfEntry instanceof LocalVariableEntry) {
562 // TODO: Implement it 585 this.renamer.setLocalVariableTreeName((LocalVariableEntry) obfEntry, newName);
563 } else { 586 } else {
564 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 587 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
565 } 588 }
566 589
567 // clear caches 590 // clear caches
@@ -575,13 +598,14 @@ public class Deobfuscator {
575 } else if (obfEntry instanceof FieldEntry) { 598 } else if (obfEntry instanceof FieldEntry) {
576 this.renamer.removeFieldMapping((FieldEntry) obfEntry); 599 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
577 } else if (obfEntry instanceof MethodEntry) { 600 } else if (obfEntry instanceof MethodEntry) {
601 if (((MethodEntry) obfEntry).isConstructor()) {
602 throw new IllegalArgumentException("Cannot rename constructors");
603 }
578 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); 604 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
579 } else if (obfEntry instanceof ConstructorEntry) { 605 } else if (obfEntry instanceof LocalVariableEntry) {
580 throw new IllegalArgumentException("Cannot rename constructors"); 606 this.renamer.removeLocalVariableMapping((LocalVariableEntry) obfEntry);
581 } else if (obfEntry instanceof ArgumentEntry) {
582 this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry);
583 } else { 607 } else {
584 throw new Error("Unknown entry type: " + obfEntry); 608 throw new Error("Unknown entry desc: " + obfEntry);
585 } 609 }
586 610
587 // clear caches 611 // clear caches
@@ -594,15 +618,15 @@ public class Deobfuscator {
594 } else if (obfEntry instanceof FieldEntry) { 618 } else if (obfEntry instanceof FieldEntry) {
595 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); 619 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
596 } else if (obfEntry instanceof MethodEntry) { 620 } else if (obfEntry instanceof MethodEntry) {
597 this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); 621 MethodEntry methodEntry = (MethodEntry) obfEntry;
598 } else if (obfEntry instanceof ConstructorEntry) { 622 if (methodEntry.isConstructor()) {
599 throw new IllegalArgumentException("Cannot rename constructors"); 623 throw new IllegalArgumentException("Cannot rename constructors");
600 } else if (obfEntry instanceof ArgumentEntry) { 624 }
601 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 625 this.renamer.markMethodTreeAsDeobfuscated(methodEntry);
602 } else if (obfEntry instanceof LocalVariableEntry) { 626 } else if (obfEntry instanceof LocalVariableEntry) {
603 // TODO: Implement it 627 this.renamer.markArgumentAsDeobfuscated((LocalVariableEntry) obfEntry);
604 } else { 628 } else {
605 throw new Error("Unknown entry type: " + obfEntry); 629 throw new Error("Unknown entry desc: " + obfEntry);
606 } 630 }
607 631
608 // clear caches 632 // clear caches
@@ -615,17 +639,33 @@ public class Deobfuscator {
615 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); 639 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
616 else if (obfEntry instanceof FieldEntry) 640 else if (obfEntry instanceof FieldEntry)
617 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); 641 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
618 else if (obfEntry instanceof BehaviorEntry) 642 else if (obfEntry instanceof MethodEntry)
619 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); 643 this.renamer.setMethodModifier((MethodEntry) obfEntry, modifierEntry);
620 else 644 else
621 throw new Error("Unknown entry type: " + obfEntry); 645 throw new Error("Unknown entry desc: " + obfEntry);
622 } 646 }
623 647
624 public Mappings.EntryModifier getModifier(Entry obEntry) { 648 public Mappings.EntryModifier getModifier(Entry obfEntry) {
625 Entry entry = obfuscateEntry(obEntry); 649 Entry entry = obfuscateEntry(obfEntry);
626 if (entry != null) 650 if (entry != null)
627 obEntry = entry; 651 obfEntry = entry;
628 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); 652 if (obfEntry instanceof ClassEntry)
653 return this.renamer.getClassModifier((ClassEntry) obfEntry);
654 else if (obfEntry instanceof FieldEntry)
655 return this.renamer.getFieldModifier((FieldEntry) obfEntry);
656 else if (obfEntry instanceof MethodEntry)
657 return this.renamer.getMethodModfifier((MethodEntry) obfEntry);
658 else
659 throw new Error("Unknown entry desc: " + obfEntry);
660 }
661
662 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){
663 List<IAstTransform> transformers = Arrays.asList(
664 new ObfuscatedEnumSwitchRewriterTransform(context)
665 );
666 for (IAstTransform transform : transformers){
667 transform.run(builder.getCompilationUnit());
668 }
629 } 669 }
630 670
631 public interface ProgressListener { 671 public interface ProgressListener {
@@ -635,6 +675,7 @@ public class Deobfuscator {
635 } 675 }
636 676
637 public interface ClassTransformer { 677 public interface ClassTransformer {
638 CtClass transform(CtClass c) throws Exception; 678 String transform(ClassNode node, ClassWriter writer);
639 } 679 }
680
640} 681}
diff --git a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
new file mode 100644
index 00000000..547ed0b2
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
@@ -0,0 +1,19 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import cuchaz.enigma.mapping.entry.ClassEntry;
5import org.objectweb.asm.ClassWriter;
6import org.objectweb.asm.tree.ClassNode;
7
8import java.util.List;
9
10/**
11 * For delegation of TranslatingTypeLoader without needing the subclass the whole thing
12 */
13public interface ITranslatingTypeLoader extends ITypeLoader {
14 List<String> getClassNamesToTry(String className);
15
16 List<String> getClassNamesToTry(ClassEntry obfClassEntry);
17
18 String transformInto(ClassNode node, ClassWriter writer);
19}
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
new file mode 100644
index 00000000..f4a7fe09
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
@@ -0,0 +1,39 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.Buffer;
4import cuchaz.enigma.mapping.entry.ClassEntry;
5import org.objectweb.asm.ClassWriter;
6import org.objectweb.asm.tree.ClassNode;
7
8import java.util.List;
9
10/**
11 * Typeloader with synchronized tryLoadType method
12 */
13public class SynchronizedTypeLoader implements ITranslatingTypeLoader {
14 private final TranslatingTypeLoader delegate;
15
16 public SynchronizedTypeLoader(TranslatingTypeLoader delegate) {
17 this.delegate = delegate;
18 }
19
20 @Override
21 public List<String> getClassNamesToTry(String className) {
22 return delegate.getClassNamesToTry(className);
23 }
24
25 @Override
26 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
27 return delegate.getClassNamesToTry(obfClassEntry);
28 }
29
30 @Override
31 public String transformInto(ClassNode node, ClassWriter writer) {
32 return delegate.transformInto(node, writer);
33 }
34
35 @Override
36 public synchronized boolean tryLoadType(String internalName, Buffer buffer) {
37 return delegate.tryLoadType(internalName, buffer);
38 }
39}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 2a2041a0..eb780ee9 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -12,106 +12,63 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import com.strobel.assembler.metadata.Buffer; 15import com.strobel.assembler.metadata.Buffer;
17import com.strobel.assembler.metadata.ClasspathTypeLoader;
18import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
19import cuchaz.enigma.analysis.BridgeMarker;
20import cuchaz.enigma.analysis.JarIndex; 17import cuchaz.enigma.analysis.JarIndex;
21import cuchaz.enigma.bytecode.translators.ClassTranslator; 18import cuchaz.enigma.analysis.ParsedJar;
22import cuchaz.enigma.bytecode.translators.InnerClassWriter; 19import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
23import cuchaz.enigma.bytecode.translators.LocalVariableTranslator; 20import cuchaz.enigma.mapping.entry.ClassEntry;
24import cuchaz.enigma.bytecode.translators.MethodParameterTranslator; 21import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
25import cuchaz.enigma.mapping.ClassEntry;
26import cuchaz.enigma.mapping.Translator; 22import cuchaz.enigma.mapping.Translator;
27import javassist.*; 23import org.objectweb.asm.ClassWriter;
28import javassist.bytecode.Descriptor; 24import org.objectweb.asm.Opcodes;
25import org.objectweb.asm.tree.ClassNode;
29 26
30import java.io.ByteArrayOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.util.List; 27import java.util.List;
34import java.util.Map;
35import java.util.jar.JarEntry;
36import java.util.jar.JarFile;
37 28
38public class TranslatingTypeLoader implements ITypeLoader { 29public class TranslatingTypeLoader extends CachingTypeLoader implements ITranslatingTypeLoader {
30 //Store one instance as the classpath shouldnt change during load
31 private static final ITypeLoader defaultTypeLoader = new CachingClasspathTypeLoader();
39 32
40 private JarFile jar; 33 private final ParsedJar jar;
41 private JarIndex jarIndex; 34 private final JarIndex jarIndex;
42 private Translator obfuscatingTranslator; 35 private final ReferencedEntryPool entryPool;
43 private Translator deobfuscatingTranslator; 36 private final Translator obfuscatingTranslator;
44 private Map<String, byte[]> cache; 37 private final Translator deobfuscatingTranslator;
45 private ClasspathTypeLoader defaultTypeLoader;
46 38
47 public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { 39 public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) {
48 this.jar = jar; 40 this.jar = jar;
49 this.jarIndex = jarIndex; 41 this.jarIndex = jarIndex;
42 this.entryPool = entryPool;
50 this.obfuscatingTranslator = obfuscatingTranslator; 43 this.obfuscatingTranslator = obfuscatingTranslator;
51 this.deobfuscatingTranslator = deobfuscatingTranslator; 44 this.deobfuscatingTranslator = deobfuscatingTranslator;
52 this.cache = Maps.newHashMap();
53 this.defaultTypeLoader = new ClasspathTypeLoader();
54
55 }
56
57 public void clearCache() {
58 this.cache.clear();
59 }
60
61 @Override
62 public boolean tryLoadType(String className, Buffer out) {
63
64 // check the cache
65 byte[] data;
66 if (this.cache.containsKey(className)) {
67 data = this.cache.get(className);
68 } else {
69 data = loadType(className);
70 this.cache.put(className, data);
71 }
72
73 if (data == null) {
74 // chain to default type loader
75 return this.defaultTypeLoader.tryLoadType(className, out);
76 }
77
78 // send the class to the decompiler
79 out.reset(data.length);
80 System.arraycopy(data, 0, out.array(), out.position(), data.length);
81 out.position(0);
82 return true;
83 } 45 }
84 46
85 public CtClass loadClass(String deobfClassName) { 47 protected byte[] doLoad(String className){
86 48 byte[] data = loadType(className);
87 byte[] data = loadType(deobfClassName);
88 if (data == null) { 49 if (data == null) {
89 return null; 50 // chain to default desc loader
90 } 51 Buffer parentBuf = new Buffer();
91 52 if (defaultTypeLoader.tryLoadType(className, parentBuf)){
92 // return a javassist handle for the class 53 return parentBuf.array();
93 String javaClassFileName = Descriptor.toJavaName(deobfClassName); 54 }
94 ClassPool classPool = new ClassPool(); 55 return EMPTY_ARRAY;//need to return *something* as null means no store
95 classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data));
96 try {
97 return classPool.get(javaClassFileName);
98 } catch (NotFoundException ex) {
99 throw new Error(ex);
100 } 56 }
57 return data;
101 } 58 }
102 59
103 private byte[] loadType(String className) { 60 private byte[] loadType(String className) {
104 61
105 // NOTE: don't know if class name is obf or deobf 62 // NOTE: don't know if class name is obf or deobf
106 ClassEntry classEntry = new ClassEntry(className); 63 ClassEntry classEntry = new ClassEntry(className);
107 ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry); 64 ClassEntry obfClassEntry = this.obfuscatingTranslator.getTranslatedClass(classEntry);
108 65
109 // is this an inner class referenced directly? (ie trying to load b instead of a$b) 66 // is this an inner class referenced directly? (ie trying to load b instead of a$b)
110 if (!obfClassEntry.isInnerClass()) { 67 if (!obfClassEntry.isInnerClass()) {
111 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry); 68 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry);
112 if (classChain.size() > 1) { 69 if (classChain.size() > 1) {
113 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", 70 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s",
114 className, obfClassEntry.buildClassEntry(classChain) 71 className, obfClassEntry.buildClassEntry(classChain)
115 )); 72 ));
116 return null; 73 return null;
117 } 74 }
@@ -126,56 +83,26 @@ public class TranslatingTypeLoader implements ITypeLoader {
126 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); 83 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName()));
127 84
128 // find the class in the jar 85 // find the class in the jar
129 String classInJarName = findClassInJar(obfClassEntry); 86 ClassNode node = findClassInJar(obfClassEntry);
130 if (classInJarName == null) { 87 if (node == null) {
131 // couldn't find it 88 // couldn't find it
132 return null; 89 return null;
133 } 90 }
134 91
135 try { 92 ClassWriter writer = new ClassWriter(0);
136 // read the class file into a buffer 93 transformInto(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
159 // sanity checking
160 assertClassName(c, classEntry);
161 94
162 // DEBUG 95 // we have a transformed class!
163 //Util.writeClass( c ); 96 return writer.toByteArray();
164
165 // we have a transformed class!
166 return c.toBytecode();
167 } catch (IOException | NotFoundException | CannotCompileException ex) {
168 throw new Error(ex);
169 }
170 } 97 }
171 98
172 private String findClassInJar(ClassEntry obfClassEntry) { 99 private ClassNode findClassInJar(ClassEntry obfClassEntry) {
173 100
174 // try to find the class in the jar 101 // try to find the class in the jar
175 for (String className : getClassNamesToTry(obfClassEntry)) { 102 for (String className : getClassNamesToTry(obfClassEntry)) {
176 JarEntry jarEntry = this.jar.getJarEntry(className + ".class"); 103 ClassNode node = this.jar.getClassNode(className);
177 if (jarEntry != null) { 104 if (node != null) {
178 return className; 105 return node;
179 } 106 }
180 } 107 }
181 108
@@ -183,10 +110,12 @@ public class TranslatingTypeLoader implements ITypeLoader {
183 return null; 110 return null;
184 } 111 }
185 112
113 @Override
186 public List<String> getClassNamesToTry(String className) { 114 public List<String> getClassNamesToTry(String className) {
187 return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className))); 115 return getClassNamesToTry(this.obfuscatingTranslator.getTranslatedClass(new ClassEntry(className)));
188 } 116 }
189 117
118 @Override
190 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 119 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
191 List<String> classNamesToTry = Lists.newArrayList(); 120 List<String> classNamesToTry = Lists.newArrayList();
192 classNamesToTry.add(obfClassEntry.getName()); 121 classNamesToTry.add(obfClassEntry.getName());
@@ -197,36 +126,10 @@ public class TranslatingTypeLoader implements ITypeLoader {
197 return classNamesToTry; 126 return classNamesToTry;
198 } 127 }
199 128
200 public CtClass transformClass(CtClass c) 129 @Override
201 throws IOException, NotFoundException, CannotCompileException { 130 public String transformInto(ClassNode node, ClassWriter writer) {
202 131 node.accept(new TranslationClassVisitor(deobfuscatingTranslator, jarIndex, entryPool, Opcodes.ASM5, writer));
203 // reconstruct inner classes 132 return deobfuscatingTranslator.getTranslatedClass(new ClassEntry(node.name)).getName();
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 } 133 }
224 134
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 }
232} 135}
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..e876bb07 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -12,8 +12,8 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.entry.MethodEntry;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
18 18
19import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
@@ -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..b8ee17da 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -12,7 +12,7 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
17 17
18import javax.swing.tree.DefaultMutableTreeNode; 18import javax.swing.tree.DefaultMutableTreeNode;
@@ -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..101729d8 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -11,9 +11,9 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.ConstructorEntry; 15import cuchaz.enigma.mapping.entry.Entry;
16import cuchaz.enigma.mapping.Entry; 16import cuchaz.enigma.mapping.entry.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..9be8378e 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
@@ -15,6 +15,7 @@ import com.google.common.collect.Lists;
15import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.mapping.*; 17import cuchaz.enigma.mapping.*;
18import cuchaz.enigma.mapping.entry.*;
18 19
19import java.util.AbstractMap; 20import java.util.AbstractMap;
20import java.util.List; 21import java.util.List;
@@ -87,18 +88,18 @@ public class EntryRenamer {
87 MethodEntry newMethodEntry = renames.get(methodEntry); 88 MethodEntry newMethodEntry = renames.get(methodEntry);
88 if (newMethodEntry != null) { 89 if (newMethodEntry != null) {
89 return (T) new MethodEntry( 90 return (T) new MethodEntry(
90 methodEntry.getClassEntry(), 91 methodEntry.getOwnerClassEntry(),
91 newMethodEntry.getName(), 92 newMethodEntry.getName(),
92 methodEntry.getSignature() 93 methodEntry.getDesc()
93 ); 94 );
94 } 95 }
95 return thing; 96 return thing;
96 } else if (thing instanceof ArgumentEntry) { 97 } else if (thing instanceof LocalVariableEntry) {
97 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 98 LocalVariableEntry variableEntry = (LocalVariableEntry) thing;
98 return (T) new ArgumentEntry( 99 return (T) new LocalVariableEntry(
99 renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), 100 renameMethodsInThing(renames, variableEntry.getOwnerEntry()),
100 argumentEntry.getIndex(), 101 variableEntry.getIndex(),
101 argumentEntry.getName() 102 variableEntry.getName()
102 ); 103 );
103 } else if (thing instanceof EntryReference) { 104 } else if (thing instanceof EntryReference) {
104 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 105 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
@@ -119,27 +120,45 @@ public class EntryRenamer {
119 } else if (thing instanceof ClassEntry) { 120 } else if (thing instanceof ClassEntry) {
120 ClassEntry classEntry = (ClassEntry) thing; 121 ClassEntry classEntry = (ClassEntry) thing;
121 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); 122 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
122 } else if (thing instanceof FieldEntry) { 123 } else if (thing instanceof FieldDefEntry) {
123 FieldEntry fieldEntry = (FieldEntry) thing; 124 FieldDefEntry fieldEntry = (FieldDefEntry) thing;
124 return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); 125 return (T) new FieldDefEntry(
125 } else if (thing instanceof ConstructorEntry) { 126 renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()),
126 ConstructorEntry constructorEntry = (ConstructorEntry) thing; 127 fieldEntry.getName(),
127 return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); 128 renameClassesInThing(renames, fieldEntry.getDesc()),
129 renameClassesInThing(renames, fieldEntry.getSignature()),
130 fieldEntry.getAccess()
131 );
132 } else if (thing instanceof MethodDefEntry) {
133 MethodDefEntry methodEntry = (MethodDefEntry) thing;
134 return (T) new MethodDefEntry(
135 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
136 methodEntry.getName(),
137 renameClassesInThing(renames, methodEntry.getDesc()),
138 renameClassesInThing(renames, methodEntry.getSignature()),
139 methodEntry.getAccess()
140 );
128 } else if (thing instanceof MethodEntry) { 141 } else if (thing instanceof MethodEntry) {
129 MethodEntry methodEntry = (MethodEntry) thing; 142 MethodEntry methodEntry = (MethodEntry) thing;
130 return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); 143 return (T) new MethodEntry(
131 } else if (thing instanceof ArgumentEntry) { 144 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
132 ArgumentEntry argumentEntry = (ArgumentEntry) thing; 145 methodEntry.getName(),
133 return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); 146 renameClassesInThing(renames, methodEntry.getDesc())
147 );
148 } else if (thing instanceof LocalVariableEntry) {
149 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
150 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName());
134 } else if (thing instanceof EntryReference) { 151 } else if (thing instanceof EntryReference) {
135 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; 152 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
136 reference.entry = renameClassesInThing(renames, reference.entry); 153 reference.entry = renameClassesInThing(renames, reference.entry);
137 reference.context = renameClassesInThing(renames, reference.context); 154 reference.context = renameClassesInThing(renames, reference.context);
138 return thing; 155 return thing;
156 } else if (thing instanceof MethodDescriptor) {
157 return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
158 } else if (thing instanceof TypeDescriptor) {
159 return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
139 } else if (thing instanceof Signature) { 160 } else if (thing instanceof Signature) {
140 return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); 161 return (T) ((Signature) thing).remap(className -> renameClassesInThing(renames, className));
141 } else if (thing instanceof Type) {
142 return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className));
143 } 162 }
144 163
145 return thing; 164 return thing;
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 34d2eff1..f63b779a 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -11,17 +11,18 @@
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; 15import cuchaz.enigma.mapping.entry.FieldEntry;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.entry.MethodDefEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
17 18
18import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
19 20
20public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { 21public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
21 22
22 private Translator deobfuscatingTranslator; 23 private Translator deobfuscatingTranslator;
23 private FieldEntry entry; 24 private FieldEntry entry;
24 private EntryReference<FieldEntry, BehaviorEntry> reference; 25 private EntryReference<FieldEntry, MethodDefEntry> reference;
25 private Access access; 26 private Access access;
26 27
27 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 28 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) {
@@ -30,7 +31,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
30 this.reference = null; 31 this.reference = null;
31 } 32 }
32 33
33 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { 34 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, Access access) {
34 this.deobfuscatingTranslator = deobfuscatingTranslator; 35 this.deobfuscatingTranslator = deobfuscatingTranslator;
35 this.entry = reference.entry; 36 this.entry = reference.entry;
36 this.reference = reference; 37 this.reference = reference;
@@ -43,34 +44,34 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
43 } 44 }
44 45
45 @Override 46 @Override
46 public EntryReference<FieldEntry, BehaviorEntry> getReference() { 47 public EntryReference<FieldEntry, MethodDefEntry> getReference() {
47 return this.reference; 48 return this.reference;
48 } 49 }
49 50
50 @Override 51 @Override
51 public String toString() { 52 public String toString() {
52 if (this.reference != null) { 53 if (this.reference != null) {
53 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); 54 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access);
54 } 55 }
55 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 56 return deobfuscatingTranslator.getTranslatedField(entry).getName();
56 } 57 }
57 58
58 public void load(JarIndex index, boolean recurse) { 59 public void load(JarIndex index, boolean recurse) {
59 // get all the child nodes 60 // get all the child nodes
60 if (this.reference == null) { 61 if (this.reference == null) {
61 for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { 62 for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) {
62 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 63 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
63 } 64 }
64 } else { 65 } else {
65 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { 66 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.reference.context)) {
66 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); 67 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context)));
67 } 68 }
68 } 69 }
69 70
70 if (recurse && children != null) { 71 if (recurse && children != null) {
71 for (Object node : children) { 72 for (Object node : children) {
72 if (node instanceof BehaviorReferenceTreeNode) { 73 if (node instanceof MethodReferenceTreeNode) {
73 ((BehaviorReferenceTreeNode) node).load(index, true); 74 ((MethodReferenceTreeNode) node).load(index, true);
74 } else if (node instanceof FieldReferenceTreeNode) { 75 } else if (node instanceof FieldReferenceTreeNode) {
75 ((FieldReferenceTreeNode) node).load(index, true); 76 ((FieldReferenceTreeNode) node).load(index, true);
76 } 77 }
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..69fe54fc
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
@@ -0,0 +1,38 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.entry.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, signature, 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, signature);
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, signature);
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..04742278
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
@@ -0,0 +1,23 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.entry.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..f37f1e90
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
@@ -0,0 +1,77 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.mapping.Signature;
6import cuchaz.enigma.mapping.entry.ClassEntry;
7import cuchaz.enigma.mapping.entry.MethodDefEntry;
8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.Handle;
10import org.objectweb.asm.MethodVisitor;
11import org.objectweb.asm.Opcodes;
12
13public class IndexReferenceVisitor extends ClassVisitor {
14 private final JarIndex index;
15 private ClassEntry classEntry;
16
17 public IndexReferenceVisitor(JarIndex index, int api) {
18 super(api);
19 this.index = index;
20 }
21
22 @Override
23 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 this.classEntry = new ClassEntry(name);
25 }
26
27 @Override
28 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
29 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
30 return new Method(this.index, entry, this.api);
31 }
32
33 private class Method extends MethodVisitor {
34 private final JarIndex index;
35 private final MethodDefEntry callerEntry;
36
37 public Method(JarIndex index, MethodDefEntry callerEntry, int api) {
38 super(api);
39 this.index = index;
40 this.callerEntry = callerEntry;
41 }
42
43 @Override
44 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
45 this.index.indexFieldAccess(callerEntry, owner, name, desc);
46 }
47
48 @Override
49 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
50 this.index.indexMethodCall(callerEntry, owner, name, desc);
51 }
52
53 @Override
54 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
55 for (Object bsmArg : bsmArgs){
56 if (bsmArg instanceof Handle){
57 Handle handle = (Handle)bsmArg;
58 switch (handle.getTag()){
59 case Opcodes.H_GETFIELD:
60 case Opcodes.H_GETSTATIC:
61 case Opcodes.H_PUTFIELD:
62 case Opcodes.H_PUTSTATIC:
63 this.index.indexFieldAccess(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
64 break;
65 case Opcodes.H_INVOKEINTERFACE:
66 case Opcodes.H_INVOKESPECIAL:
67 case Opcodes.H_INVOKESTATIC:
68 case Opcodes.H_INVOKEVIRTUAL:
69 case Opcodes.H_NEWINVOKESPECIAL:
70 this.index.indexMethodCall(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
71 break;
72 }
73 }
74 }
75 }
76 }
77}
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..5917a32f 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -12,113 +12,73 @@
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 cuchaz.enigma.mapping.entry.*;
17import javassist.*; 18import org.objectweb.asm.Opcodes;
18import javassist.bytecode.*;
19import javassist.expr.*;
20 19
21import java.lang.reflect.Modifier;
22import java.util.*; 20import java.util.*;
23import java.util.jar.JarFile;
24 21
25public class JarIndex { 22public class JarIndex {
26 23
24 private final ReferencedEntryPool entryPool;
25
27 private Set<ClassEntry> obfClassEntries; 26 private Set<ClassEntry> obfClassEntries;
28 private TranslationIndex translationIndex; 27 private TranslationIndex translationIndex;
29 private Map<Entry, Access> access; 28 private Map<Entry, Access> access;
30 private Multimap<ClassEntry, FieldEntry> fields; 29 private Multimap<ClassEntry, FieldDefEntry> fields;
31 private Multimap<ClassEntry, BehaviorEntry> behaviors; 30 private Multimap<ClassEntry, MethodDefEntry> methods;
32 private Multimap<String, MethodEntry> methodImplementations; 31 private Multimap<String, MethodDefEntry> methodImplementations;
33 private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; 32 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodsReferencing;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; 33 private Multimap<MethodEntry, MethodEntry> methodReferences;
34 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; 35 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
36 private Map<ClassEntry, ClassEntry> outerClassesByInner; 36 private Map<ClassEntry, ClassEntry> outerClassesByInner;
37 private Map<ClassEntry, BehaviorEntry> anonymousClasses;
38 private Map<MethodEntry, MethodEntry> bridgedMethods; 37 private Map<MethodEntry, MethodEntry> bridgedMethods;
39 private Set<MethodEntry> syntheticMethods; 38 private Set<MethodEntry> syntheticMethods;
40 39
41 public JarIndex() { 40 public JarIndex(ReferencedEntryPool entryPool) {
41 this.entryPool = entryPool;
42 this.obfClassEntries = Sets.newHashSet(); 42 this.obfClassEntries = Sets.newHashSet();
43 this.translationIndex = new TranslationIndex(); 43 this.translationIndex = new TranslationIndex(entryPool);
44 this.access = Maps.newHashMap(); 44 this.access = Maps.newHashMap();
45 this.fields = HashMultimap.create(); 45 this.fields = HashMultimap.create();
46 this.behaviors = HashMultimap.create(); 46 this.methods = HashMultimap.create();
47 this.methodImplementations = HashMultimap.create(); 47 this.methodImplementations = HashMultimap.create();
48 this.behaviorReferences = HashMultimap.create(); 48 this.methodsReferencing = HashMultimap.create();
49 this.methodReferences = HashMultimap.create();
49 this.fieldReferences = HashMultimap.create(); 50 this.fieldReferences = HashMultimap.create();
50 this.innerClassesByOuter = HashMultimap.create(); 51 this.innerClassesByOuter = HashMultimap.create();
51 this.outerClassesByInner = Maps.newHashMap(); 52 this.outerClassesByInner = Maps.newHashMap();
52 this.anonymousClasses = Maps.newHashMap();
53 this.bridgedMethods = Maps.newHashMap(); 53 this.bridgedMethods = Maps.newHashMap();
54 this.syntheticMethods = Sets.newHashSet(); 54 this.syntheticMethods = Sets.newHashSet();
55 } 55 }
56 56
57 public void indexJar(JarFile jar, boolean buildInnerClasses) { 57 public void indexJar(ParsedJar jar, boolean buildInnerClasses) {
58 58
59 // step 1: read the class names 59 // step 1: read the class names
60 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); 60 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 61
76 // step 3: index extends, implements, fields, and methods 62 // step 2: index classes, fields, methods, interfaces
77 for (CtClass c : JarClassIterator.classes(jar)) { 63 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 64
92 // step 4: index field, method, constructor references 65 // step 3: index field, method, constructor references
93 for (CtClass c : JarClassIterator.classes(jar)) { 66 jar.visit(node -> node.accept(new IndexReferenceVisitor(this, Opcodes.ASM5)));
94 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 67
95 indexBehaviorReferences(behavior); 68 // step 4: index access and bridged methods
69 for (MethodDefEntry methodEntry : methods.values()) {
70 // look for access and bridged methods
71 MethodEntry accessedMethod = findAccessMethod(methodEntry);
72 if (accessedMethod != null) {
73 if (isBridgedMethod(accessedMethod, methodEntry)) {
74 this.bridgedMethods.put(methodEntry, accessedMethod);
75 }
96 } 76 }
97 } 77 }
98 78
99 if (buildInnerClasses) { 79 if (buildInnerClasses) {
100
101 // step 5: index inner classes and anonymous classes 80 // step 5: index inner classes and anonymous classes
102 for (CtClass c : JarClassIterator.classes(jar)) { 81 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 82
123 // step 6: update other indices with inner class info 83 // step 6: update other indices with inner class info
124 Map<String, String> renames = Maps.newHashMap(); 84 Map<String, String> renames = Maps.newHashMap();
@@ -133,385 +93,138 @@ public class JarIndex {
133 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); 93 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
134 this.translationIndex.renameClasses(renames); 94 this.translationIndex.renameClasses(renames);
135 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); 95 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
136 EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); 96 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing);
97 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
137 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); 98 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
138 EntryRenamer.renameClassesInMap(renames, this.access); 99 EntryRenamer.renameClassesInMap(renames, this.access);
139 } 100 }
140 } 101 }
141 102
142 private void indexBehavior(CtBehavior behavior) { 103 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
143 // get the behavior entry 104 for (String interfaceName : interfaces) {
144 final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 105 if (name.equals(interfaceName)) {
145 if (behaviorEntry instanceof MethodEntry) { 106 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 } 107 }
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 }
161 }
162 // looks like we don't care about constructors here
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 } 108 }
109 return this.translationIndex.indexClass(access, name, signature, superName, interfaces);
229 } 110 }
230 111
231 private CtMethod getBridgedMethod(CtMethod method) { 112 protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) {
232 113 FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
233 // bridge methods just call another method, cast it to the return type, and return the result 114 this.translationIndex.indexField(fieldEntry);
234 // let's see if we can detect this scenario 115 this.access.put(fieldEntry, Access.get(access));
235 116 this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
236 // skip non-synthetic methods 117 }
237 if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) {
238 return null;
239 }
240 118
241 // get all the called methods 119 protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) {
242 final List<MethodCall> methodCalls = Lists.newArrayList(); 120 MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
243 try { 121 this.translationIndex.indexMethod(methodEntry);
244 method.instrument(new ExprEditor() { 122 this.access.put(methodEntry, Access.get(access));
245 @Override 123 this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry);
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 124
255 // is there just one? 125 if (new AccessFlags(access).isSynthetic()) {
256 if (methodCalls.size() != 1) { 126 syntheticMethods.add(methodEntry);
257 return null;
258 } 127 }
259 MethodCall call = methodCalls.get(0);
260 128
261 try { 129 // we don't care about constructors here
262 // we have a bridge method! 130 if (!methodEntry.isConstructor()) {
263 return call.getMethod(); 131 // index implementation
264 } catch (NotFoundException ex) { 132 this.methodImplementations.put(methodEntry.getClassName(), methodEntry);
265 // can't find the type? not a bridge method
266 return null;
267 } 133 }
268 } 134 }
269 135
270 private ClassEntry findOuterClass(CtClass c) { 136 protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) {
271 137 MethodEntry referencedMethod = new MethodEntry(entryPool.getClass(owner), name, new MethodDescriptor(desc));
272 ClassEntry classEntry = EntryFactory.getClassEntry(c); 138 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod);
273 139 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
274 // does this class already have an outer class? 140 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
275 if (classEntry.isInnerClass()) {
276 return classEntry.getOuterClassEntry();
277 } 141 }
278 142 methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
279 // inner classes: 143 methodReferences.put(callerEntry, referencedMethod);
280 // have constructors that can (illegally) set synthetic fields
281 // the outer class is the only class that calls constructors
282
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 }
346
347 return null;
348 } 144 }
349 145
350 private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { 146 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
351 147 FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc));
352 // clearly this would be silly 148 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField);
353 if (outerClassEntry.equals(innerClassEntry)) { 149 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) {
354 return false; 150 referencedField = referencedField.updateOwnership(resolvedClassEntry);
355 } 151 }
356 152 fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry));
357 // is the outer class in the jar?
358 return this.obfClassEntries.contains(outerClassEntry);
359
360 } 153 }
361 154
362 @SuppressWarnings("unchecked") 155 public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) {
363 private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { 156 this.innerClassesByOuter.put(outerEntry, innerEntry);
157 this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry);
158 }
364 159
365 // illegal constructors only set synthetic member fields, then call super() 160 private MethodEntry findAccessMethod(MethodDefEntry method) {
366 String className = constructor.getDeclaringClass().getName();
367 161
368 // collect all the field accesses, constructor calls, and method calls 162 // we want to find all compiler-added methods that directly call another with no processing
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 163
380 @Override 164 // skip non-synthetic methods
381 public void edit(ConstructorCall constructorCall) { 165 if (!method.getAccess().isSynthetic()) {
382 constructorCalls.add(constructorCall); 166 return null;
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 } 167 }
394 168
395 // are all the writes to synthetic fields? 169 // get all the methods that we call
396 for (FieldAccess fieldWrite : illegalFieldWrites) { 170 final Collection<MethodEntry> referencedMethods = methodReferences.get(method);
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 171
417 // is this field synthetic? 172 // is there just one?
418 boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 173 if (referencedMethods.size() != 1) {
419 if (isSynthetic) { 174 return null;
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 } 175 }
426 176
427 // we passed all the tests! 177 return referencedMethods.stream().findFirst().orElse(null);
428 return true;
429 } 178 }
430 179
431 private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { 180 private boolean isBridgedMethod(MethodEntry called, MethodEntry access) {
432 181 // Bridged methods will always have the same name as the method they are calling
433 // is this class already marked anonymous? 182 // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed)
434 EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); 183 if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) {
435 if (enclosingMethodAttribute != null) { 184 return false;
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 } 185 }
453 186
454 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 187 TypeDescriptor accessReturn = access.getDesc().getReturnDesc();
455 188 TypeDescriptor calledReturn = called.getDesc().getReturnDesc();
456 // anonymous classes: 189 if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) {
457 // can't be abstract 190 return false;
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
462 // is abstract?
463 if (Modifier.isAbstract(c.getModifiers())) {
464 return null;
465 } 191 }
466 192
467 // is there exactly one constructor? 193 // Bridged methods will never have the same type as what they are calling
468 if (c.getDeclaredConstructors().length != 1) { 194 if (accessReturn.equals(calledReturn)) {
469 return null; 195 return false;
470 } 196 }
471 CtConstructor constructor = c.getDeclaredConstructors()[0];
472 197
473 // is this constructor called exactly once? 198 String accessType = accessReturn.toString();
474 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
475 Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry);
476 if (references.size() != 1) {
477 return null;
478 }
479 199
480 // does the caller use this type? 200 // If we're casting down from generic type to type-erased Object we're a bridge method
481 BehaviorEntry caller = references.iterator().next().context; 201 if (accessType.equals("Ljava/lang/Object;")) {
482 for (FieldEntry fieldEntry : getReferencedFields(caller)) { 202 return true;
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 } 203 }
493 204
494 return caller; 205 // Now we need to detect cases where we are being casted down to a higher type bound
206 List<ClassEntry> calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry());
207 return calledAncestry.contains(accessReturn.getTypeEntry());
495 } 208 }
496 209
497 public Set<ClassEntry> getObfClassEntries() { 210 public Set<ClassEntry> getObfClassEntries() {
498 return this.obfClassEntries; 211 return this.obfClassEntries;
499 } 212 }
500 213
501 public Collection<FieldEntry> getObfFieldEntries() { 214 public Collection<FieldDefEntry> getObfFieldEntries() {
502 return this.fields.values(); 215 return this.fields.values();
503 } 216 }
504 217
505 public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { 218 public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) {
506 return this.fields.get(classEntry); 219 return this.fields.get(classEntry);
507 } 220 }
508 221
509 public Collection<BehaviorEntry> getObfBehaviorEntries() { 222 public Collection<MethodDefEntry> getObfBehaviorEntries() {
510 return this.behaviors.values(); 223 return this.methods.values();
511 } 224 }
512 225
513 public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { 226 public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) {
514 return this.behaviors.get(classEntry); 227 return this.methods.get(classEntry);
515 } 228 }
516 229
517 public TranslationIndex getTranslationIndex() { 230 public TranslationIndex getTranslationIndex() {
@@ -533,8 +246,8 @@ public class JarIndex {
533 } 246 }
534 } 247 }
535 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 248 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
536 deobfuscatingTranslator, 249 deobfuscatingTranslator,
537 ancestry.get(ancestry.size() - 1) 250 ancestry.get(ancestry.size() - 1)
538 ); 251 );
539 252
540 // expand all children recursively 253 // expand all children recursively
@@ -557,28 +270,20 @@ public class JarIndex {
557 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 270 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
558 271
559 // travel to the ancestor implementation 272 // travel to the ancestor implementation
560 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); 273 ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry();
561 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { 274 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getOwnerClassEntry())) {
562 MethodEntry ancestorMethodEntry = new MethodEntry( 275 MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
563 new ClassEntry(ancestorClassEntry), 276 if (ancestorMethodEntry != null && containsObfMethod(ancestorMethodEntry)) {
564 obfMethodEntry.getName(),
565 obfMethodEntry.getSignature()
566 );
567 if (containsObfBehavior(ancestorMethodEntry)) {
568 baseImplementationClassEntry = ancestorClassEntry; 277 baseImplementationClassEntry = ancestorClassEntry;
569 } 278 }
570 } 279 }
571 280
572 // make a root node at the base 281 // make a root node at the base
573 MethodEntry methodEntry = new MethodEntry( 282 MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
574 baseImplementationClassEntry,
575 obfMethodEntry.getName(),
576 obfMethodEntry.getSignature()
577 );
578 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( 283 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
579 deobfuscatingTranslator, 284 deobfuscatingTranslator,
580 methodEntry, 285 methodEntry,
581 containsObfBehavior(methodEntry) 286 containsObfMethod(methodEntry)
582 ); 287 );
583 288
584 // expand the full tree 289 // expand the full tree
@@ -599,12 +304,8 @@ public class JarIndex {
599 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { 304 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
600 305
601 // is this method defined in this interface? 306 // is this method defined in this interface?
602 MethodEntry methodInterface = new MethodEntry( 307 MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
603 interfaceEntry, 308 if (methodInterface != null && containsObfMethod(methodInterface)) {
604 obfMethodEntry.getName(),
605 obfMethodEntry.getSignature()
606 );
607 if (containsObfBehavior(methodInterface)) {
608 interfaceMethodEntries.add(methodInterface); 309 interfaceMethodEntries.add(methodInterface);
609 } 310 }
610 } 311 }
@@ -623,27 +324,30 @@ public class JarIndex {
623 324
624 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { 325 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
625 Set<MethodEntry> methodEntries = Sets.newHashSet(); 326 Set<MethodEntry> methodEntries = Sets.newHashSet();
626 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); 327 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry));
627 return methodEntries; 328 return methodEntries;
628 } 329 }
629 330
630 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { 331 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
631 MethodEntry methodEntry = node.getMethodEntry(); 332 MethodEntry methodEntry = node.getMethodEntry();
333 if (methodEntries.contains(methodEntry)) {
334 return;
335 }
632 336
633 if (containsObfBehavior(methodEntry)) { 337 if (containsObfMethod(methodEntry)) {
634 // collect the entry 338 // collect the entry
635 methodEntries.add(methodEntry); 339 methodEntries.add(methodEntry);
636 } 340 }
637 341
638 // look at bridged methods! 342 // look at bridge methods!
639 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 343 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
640 while (bridgedEntry != null) { 344 while (bridgedMethod != null) {
641 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 345 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
642 bridgedEntry = getBridgedMethod(bridgedEntry); 346 bridgedMethod = getBridgedMethod(bridgedMethod);
643 } 347 }
644 348
645 // look at interface methods too 349 // look at interface methods too
646 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { 350 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) {
647 getRelatedMethodImplementations(methodEntries, implementationsNode); 351 getRelatedMethodImplementations(methodEntries, implementationsNode);
648 } 352 }
649 353
@@ -655,16 +359,16 @@ public class JarIndex {
655 359
656 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { 360 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
657 MethodEntry methodEntry = node.getMethodEntry(); 361 MethodEntry methodEntry = node.getMethodEntry();
658 if (containsObfBehavior(methodEntry)) { 362 if (containsObfMethod(methodEntry)) {
659 // collect the entry 363 // collect the entry
660 methodEntries.add(methodEntry); 364 methodEntries.add(methodEntry);
661 } 365 }
662 366
663 // look at bridged methods! 367 // look at bridge methods!
664 MethodEntry bridgedEntry = getBridgedMethod(methodEntry); 368 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
665 while (bridgedEntry != null) { 369 while (bridgedMethod != null) {
666 methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); 370 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
667 bridgedEntry = getBridgedMethod(bridgedEntry); 371 bridgedMethod = getBridgedMethod(bridgedMethod);
668 } 372 }
669 373
670 // recurse 374 // recurse
@@ -673,34 +377,27 @@ public class JarIndex {
673 } 377 }
674 } 378 }
675 379
676 public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { 380 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) {
677 return this.fieldReferences.get(fieldEntry); 381 return this.fieldReferences.get(fieldEntry);
678 } 382 }
679 383
680 public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { 384 public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) {
681 // linear search is fast enough for now 385 // linear search is fast enough for now
682 Set<FieldEntry> fieldEntries = Sets.newHashSet(); 386 Set<FieldEntry> fieldEntries = Sets.newHashSet();
683 for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { 387 for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) {
684 if (reference.context == behaviorEntry) { 388 if (reference.context == methodEntry) {
685 fieldEntries.add(reference.entry); 389 fieldEntries.add(reference.entry);
686 } 390 }
687 } 391 }
688 return fieldEntries; 392 return fieldEntries;
689 } 393 }
690 394
691 public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { 395 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry) {
692 return this.behaviorReferences.get(behaviorEntry); 396 return this.methodsReferencing.get(methodEntry);
693 } 397 }
694 398
695 public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { 399 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
696 // linear search is fast enough for now 400 return this.methodReferences.get(methodEntry);
697 Set<BehaviorEntry> behaviorEntries = Sets.newHashSet();
698 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) {
699 if (reference.context == behaviorEntry) {
700 behaviorEntries.add(reference.entry);
701 }
702 }
703 return behaviorEntries;
704 } 401 }
705 402
706 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { 403 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
@@ -711,22 +408,13 @@ public class JarIndex {
711 return this.outerClassesByInner.get(obfInnerClassEntry); 408 return this.outerClassesByInner.get(obfInnerClassEntry);
712 } 409 }
713 410
714 public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) {
715 return this.anonymousClasses.containsKey(obfInnerClassEntry);
716 }
717
718 public boolean isSyntheticMethod(MethodEntry methodEntry) { 411 public boolean isSyntheticMethod(MethodEntry methodEntry) {
719 return this.syntheticMethods.contains(methodEntry); 412 return this.syntheticMethods.contains(methodEntry);
720 } 413 }
721 414
722 public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) {
723 return this.anonymousClasses.get(obfInnerClassName);
724 }
725
726 public Set<ClassEntry> getInterfaces(String className) { 415 public Set<ClassEntry> getInterfaces(String className) {
727 ClassEntry classEntry = new ClassEntry(className); 416 ClassEntry classEntry = entryPool.getClass(className);
728 Set<ClassEntry> interfaces = new HashSet<>(); 417 Set<ClassEntry> interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry));
729 interfaces.addAll(this.translationIndex.getInterfaces(classEntry));
730 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { 418 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
731 interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); 419 interfaces.addAll(this.translationIndex.getInterfaces(ancestor));
732 } 420 }
@@ -754,7 +442,7 @@ public class JarIndex {
754 } 442 }
755 443
756 public boolean isInterface(String className) { 444 public boolean isInterface(String className) {
757 return this.translationIndex.isInterface(new ClassEntry(className)); 445 return this.translationIndex.isInterface(entryPool.getClass(className));
758 } 446 }
759 447
760 public boolean containsObfClass(ClassEntry obfClassEntry) { 448 public boolean containsObfClass(ClassEntry obfClassEntry) {
@@ -765,8 +453,8 @@ public class JarIndex {
765 return this.access.containsKey(obfFieldEntry); 453 return this.access.containsKey(obfFieldEntry);
766 } 454 }
767 455
768 public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { 456 public boolean containsObfMethod(MethodEntry obfMethodEntry) {
769 return this.access.containsKey(obfBehaviorEntry); 457 return this.access.containsKey(obfMethodEntry);
770 } 458 }
771 459
772 public boolean containsEntryWithSameName(Entry entry) { 460 public boolean containsEntryWithSameName(Entry entry) {
@@ -776,15 +464,13 @@ public class JarIndex {
776 return false; 464 return false;
777 } 465 }
778 466
779 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 467 public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) {
780 // check the behavior 468 // check the behavior
781 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 469 if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) {
782 return false; 470 return false;
783 } 471 }
784 472
785 // check the argument 473 return true;
786 return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size();
787
788 } 474 }
789 475
790 public boolean containsObfEntry(Entry obfEntry) { 476 public boolean containsObfEntry(Entry obfEntry) {
@@ -792,15 +478,12 @@ public class JarIndex {
792 return containsObfClass((ClassEntry) obfEntry); 478 return containsObfClass((ClassEntry) obfEntry);
793 } else if (obfEntry instanceof FieldEntry) { 479 } else if (obfEntry instanceof FieldEntry) {
794 return containsObfField((FieldEntry) obfEntry); 480 return containsObfField((FieldEntry) obfEntry);
795 } else if (obfEntry instanceof BehaviorEntry) { 481 } else if (obfEntry instanceof MethodEntry) {
796 return containsObfBehavior((BehaviorEntry) obfEntry); 482 return containsObfMethod((MethodEntry) obfEntry);
797 } else if (obfEntry instanceof ArgumentEntry) {
798 return containsObfArgument((ArgumentEntry) obfEntry);
799 } else if (obfEntry instanceof LocalVariableEntry) { 483 } else if (obfEntry instanceof LocalVariableEntry) {
800 // TODO: Implement it 484 return containsObfVariable((LocalVariableEntry) obfEntry);
801 return false;
802 } else { 485 } else {
803 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 486 throw new Error("Entry desc not supported: " + obfEntry.getClass().getName());
804 } 487 }
805 } 488 }
806 489
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index bacb1aac..723fffed 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -12,8 +12,8 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.entry.MethodEntry;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
18 18
19import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
@@ -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..904e5945 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -12,8 +12,8 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.MethodEntry; 16import cuchaz.enigma.mapping.entry.MethodEntry;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.Translator;
18 18
19import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
@@ -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..76c73c15 100644
--- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -12,30 +12,31 @@
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; 16import cuchaz.enigma.mapping.entry.Entry;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.mapping.entry.MethodDefEntry;
18import cuchaz.enigma.mapping.entry.MethodEntry;
18 19
19import javax.swing.tree.DefaultMutableTreeNode; 20import javax.swing.tree.DefaultMutableTreeNode;
20import javax.swing.tree.TreeNode; 21import javax.swing.tree.TreeNode;
21import java.util.Set; 22import java.util.Set;
22 23
23public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode 24public class MethodReferenceTreeNode extends DefaultMutableTreeNode
24 implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> { 25 implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
25 26
26 private Translator deobfuscatingTranslator; 27 private Translator deobfuscatingTranslator;
27 private BehaviorEntry entry; 28 private MethodEntry entry;
28 private EntryReference<BehaviorEntry, BehaviorEntry> reference; 29 private EntryReference<MethodEntry, MethodDefEntry> reference;
29 private Access access; 30 private Access access;
30 31
31 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { 32 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) {
32 this.deobfuscatingTranslator = deobfuscatingTranslator; 33 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.entry = entry; 34 this.entry = entry;
34 this.reference = null; 35 this.reference = null;
35 } 36 }
36 37
37 public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, 38 public MethodReferenceTreeNode(Translator deobfuscatingTranslator,
38 EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) { 39 EntryReference<MethodEntry, MethodDefEntry> reference, Access access) {
39 this.deobfuscatingTranslator = deobfuscatingTranslator; 40 this.deobfuscatingTranslator = deobfuscatingTranslator;
40 this.entry = reference.entry; 41 this.entry = reference.entry;
41 this.reference = reference; 42 this.reference = reference;
@@ -43,42 +44,42 @@ public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode
43 } 44 }
44 45
45 @Override 46 @Override
46 public BehaviorEntry getEntry() { 47 public MethodEntry getEntry() {
47 return this.entry; 48 return this.entry;
48 } 49 }
49 50
50 @Override 51 @Override
51 public EntryReference<BehaviorEntry, BehaviorEntry> getReference() { 52 public EntryReference<MethodEntry, MethodDefEntry> getReference() {
52 return this.reference; 53 return this.reference;
53 } 54 }
54 55
55 @Override 56 @Override
56 public String toString() { 57 public String toString() {
57 if (this.reference != null) { 58 if (this.reference != null) {
58 return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), 59 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context),
59 this.access); 60 this.access);
60 } 61 }
61 return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); 62 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 } 63 }
63 64
64 public void load(JarIndex index, boolean recurse) { 65 public void load(JarIndex index, boolean recurse) {
65 // get all the child nodes 66 // get all the child nodes
66 for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) { 67 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry)) {
67 add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); 68 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry)));
68 } 69 }
69 70
70 if (recurse && this.children != null) { 71 if (recurse && this.children != null) {
71 for (Object child : this.children) { 72 for (Object child : this.children) {
72 if (child instanceof BehaviorReferenceTreeNode) { 73 if (child instanceof MethodReferenceTreeNode) {
73 BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; 74 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child;
74 75
75 // don't recurse into ancestor 76 // don't recurse into ancestor
76 Set<Entry> ancestors = Sets.newHashSet(); 77 Set<Entry> ancestors = Sets.newHashSet();
77 TreeNode n = node; 78 TreeNode n = node;
78 while (n.getParent() != null) { 79 while (n.getParent() != null) {
79 n = n.getParent(); 80 n = n.getParent();
80 if (n instanceof BehaviorReferenceTreeNode) { 81 if (n instanceof MethodReferenceTreeNode) {
81 ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); 82 ancestors.add(((MethodReferenceTreeNode) n).getEntry());
82 } 83 }
83 } 84 }
84 if (ancestors.contains(node.getEntry())) { 85 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..55f2141b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -0,0 +1,95 @@
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.entry.ClassEntry;
15import org.objectweb.asm.ClassReader;
16import org.objectweb.asm.tree.ClassNode;
17
18import java.io.BufferedInputStream;
19import java.io.IOException;
20import java.io.InputStream;
21import java.util.*;
22import java.util.function.Consumer;
23import java.util.jar.JarEntry;
24import java.util.jar.JarFile;
25import java.util.jar.JarInputStream;
26
27public class ParsedJar {
28 private final Map<String, ClassNode> nodes = new LinkedHashMap<>();
29
30 public ParsedJar(JarFile jar) throws IOException {
31 try {
32 // get the jar entries that correspond to classes
33 Enumeration<JarEntry> entries = jar.entries();
34 while (entries.hasMoreElements()) {
35 JarEntry entry = entries.nextElement();
36 // is this a class file?
37 if (entry.getName().endsWith(".class")) {
38 try (InputStream input = new BufferedInputStream(jar.getInputStream(entry))) {
39 // read the ClassNode from the jar
40 ClassReader reader = new ClassReader(input);
41 ClassNode node = new ClassNode();
42 reader.accept(node, 0);
43 String path = entry.getName().substring(0, entry.getName().length() - ".class".length());
44 nodes.put(path, node);
45 }
46 }
47 }
48 } finally {
49 jar.close();
50 }
51 }
52
53 public ParsedJar(JarInputStream jar) throws IOException {
54 try {
55 // get the jar entries that correspond to classes
56 JarEntry entry;
57 while ((entry = jar.getNextJarEntry()) != null) {
58 // is this a class file?
59 if (entry.getName().endsWith(".class")) {
60 // read the ClassNode from the jar
61 ClassReader reader = new ClassReader(jar);
62 ClassNode node = new ClassNode();
63 reader.accept(node, 0);
64 String path = entry.getName().substring(0, entry.getName().length() - ".class".length());
65 nodes.put(path, node);
66 jar.closeEntry();
67 }
68 }
69 } finally {
70 jar.close();
71 }
72 }
73
74 public void visit(Consumer<ClassNode> visitor) {
75 for (ClassNode node : nodes.values()) {
76 visitor.accept(node);
77 }
78 }
79
80 public int getClassCount() {
81 return nodes.size();
82 }
83
84 public List<ClassEntry> getClassEntries() {
85 List<ClassEntry> entries = new ArrayList<>(nodes.size());
86 for (ClassNode node : nodes.values()) {
87 entries.add(new ClassEntry(node.name));
88 }
89 return entries;
90 }
91
92 public ClassNode getClassNode(String name) {
93 return nodes.get(name);
94 }
95}
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index 04693637..3950d165 100644
--- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.Entry; 14import cuchaz.enigma.mapping.entry.Entry;
15 15
16public interface ReferenceTreeNode<E extends Entry, C extends Entry> { 16public interface ReferenceTreeNode<E extends Entry, C extends Entry> {
17 E getEntry(); 17 E getEntry();
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 19250c8d..14b2e768 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -18,7 +18,7 @@ import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region; 18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.Identifier; 20import com.strobel.decompiler.languages.java.ast.Identifier;
21import cuchaz.enigma.mapping.Entry; 21import cuchaz.enigma.mapping.entry.Entry;
22 22
23import java.util.Collection; 23import java.util.Collection;
24import java.util.List; 24import java.util.List;
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index b13415da..dd5bcef0 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -17,14 +17,21 @@ import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference; 17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.mapping.*; 20import cuchaz.enigma.bytecode.AccessFlags;
21import cuchaz.enigma.mapping.Signature;
22import cuchaz.enigma.mapping.entry.*;
21 23
22public class SourceIndexClassVisitor extends SourceIndexVisitor { 24public class SourceIndexClassVisitor extends SourceIndexVisitor {
25 private final ReferencedEntryPool entryPool;
26 private final ProcyonEntryFactory entryFactory;
23 27
24 private ClassEntry classEntry; 28 private ClassDefEntry classEntry;
25 private boolean isEnum; 29 private boolean isEnum;
26 30
27 public SourceIndexClassVisitor(ClassEntry classEntry) { 31 public SourceIndexClassVisitor(ReferencedEntryPool entryPool, ClassDefEntry classEntry) {
32 super(entryPool);
33 this.entryPool = entryPool;
34 this.entryFactory = new ProcyonEntryFactory(entryPool);
28 this.classEntry = classEntry; 35 this.classEntry = classEntry;
29 } 36 }
30 37
@@ -32,11 +39,11 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
32 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 39 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
33 // is this this class, or a subtype? 40 // is this this class, or a subtype?
34 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 41 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
35 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 42 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
36 if (!classEntry.equals(this.classEntry)) { 43 if (!classEntry.equals(this.classEntry)) {
37 // it's a sub-type, recurse 44 // it's a subtype, recurse
38 index.addDeclaration(node.getNameToken(), classEntry); 45 index.addDeclaration(node.getNameToken(), classEntry);
39 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 46 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
40 } 47 }
41 48
42 return recurse(node, index); 49 return recurse(node, index);
@@ -56,31 +63,28 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
56 @Override 63 @Override
57 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { 64 public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) {
58 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 65 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
59 BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); 66 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
60 AstNode tokenNode = node.getNameToken(); 67 AstNode tokenNode = node.getNameToken();
61 if (behaviorEntry instanceof ConstructorEntry) { 68 if (methodEntry.isConstructor() && methodEntry.getName().equals("<clinit>")) {
62 ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; 69 // for static initializers, check elsewhere for the token node
63 if (constructorEntry.isStatic()) { 70 tokenNode = node.getModifiers().firstOrNullObject();
64 // for static initializers, check elsewhere for the token node
65 tokenNode = node.getModifiers().firstOrNullObject();
66 }
67 } 71 }
68 index.addDeclaration(tokenNode, behaviorEntry); 72 index.addDeclaration(tokenNode, methodEntry);
69 return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry, false), index); 73 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index);
70 } 74 }
71 75
72 @Override 76 @Override
73 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { 77 public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) {
74 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 78 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
75 ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); 79 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
76 index.addDeclaration(node.getNameToken(), constructorEntry); 80 index.addDeclaration(node.getNameToken(), methodEntry);
77 return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry, isEnum), index); 81 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index);
78 } 82 }
79 83
80 @Override 84 @Override
81 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { 85 public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) {
82 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 86 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
83 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 87 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
84 assert (node.getVariables().size() == 1); 88 assert (node.getVariables().size() == 1);
85 VariableInitializer variable = node.getVariables().firstOrNullObject(); 89 VariableInitializer variable = node.getVariables().firstOrNullObject();
86 index.addDeclaration(variable.getNameToken(), fieldEntry); 90 index.addDeclaration(variable.getNameToken(), fieldEntry);
@@ -92,7 +96,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
92 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { 96 public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) {
93 // treat enum declarations as field declarations 97 // treat enum declarations as field declarations
94 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); 98 FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION);
95 FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); 99 FieldDefEntry fieldEntry = entryFactory.getFieldDefEntry(def);
96 index.addDeclaration(node.getNameToken(), fieldEntry); 100 index.addDeclaration(node.getNameToken(), fieldEntry);
97 this.isEnum = true; 101 this.isEnum = true;
98 return recurse(node, index); 102 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..83fe296c 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -13,31 +13,33 @@ package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.HashMultimap; 14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap; 15import com.google.common.collect.Multimap;
16import com.strobel.assembler.metadata.MemberReference; 16import com.strobel.assembler.metadata.*;
17import com.strobel.assembler.metadata.MethodReference; 17import com.strobel.decompiler.ast.Variable;
18import com.strobel.assembler.metadata.ParameterDefinition;
19import com.strobel.assembler.metadata.TypeReference;
20import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
21import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
22import cuchaz.enigma.mapping.*; 20import cuchaz.enigma.mapping.TypeDescriptor;
23import javassist.bytecode.Descriptor; 21import cuchaz.enigma.mapping.entry.*;
24 22
23import java.lang.Error;
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 final ClassDefEntry ownerEntry;
32 private final MethodDefEntry methodEntry;
30 33
31 // TODO: Really fix Procyon index problem with inner classes
32 private int argumentPosition;
33 private int localsPosition;
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 35 private Map<String, Entry> identifierEntryCache = new HashMap<>();
36 36
37 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry, boolean isEnum) { 37 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, ClassDefEntry ownerEntry, MethodDefEntry methodEntry) {
38 this.behaviorEntry = behaviorEntry; 38 super(entryPool);
39 this.argumentPosition = isEnum ? 2 : 0; 39 this.entryPool = entryPool;
40 this.localsPosition = 0; 40 this.entryFactory = new ProcyonEntryFactory(entryPool);
41 this.ownerEntry = ownerEntry;
42 this.methodEntry = methodEntry;
41 } 43 }
42 44
43 @Override 45 @Override
@@ -45,19 +47,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
45 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 47 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
46 48
47 // get the behavior entry 49 // get the behavior entry
48 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 50 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
49 BehaviorEntry behaviorEntry = null; 51 MethodEntry methodEntry = null;
50 if (ref instanceof MethodReference) { 52 if (ref instanceof MethodReference) {
51 MethodReference methodRef = (MethodReference) ref; 53 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 } 54 }
60 if (behaviorEntry != null) { 55 if (methodEntry != null) {
61 // get the node for the token 56 // get the node for the token
62 AstNode tokenNode = null; 57 AstNode tokenNode = null;
63 if (node.getTarget() instanceof MemberReferenceExpression) { 58 if (node.getTarget() instanceof MemberReferenceExpression) {
@@ -68,13 +63,13 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
68 tokenNode = node.getTarget(); 63 tokenNode = node.getTarget();
69 } 64 }
70 if (tokenNode != null) { 65 if (tokenNode != null) {
71 index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); 66 index.addReference(tokenNode, methodEntry, this.methodEntry);
72 } 67 }
73 } 68 }
74 69
75 // Check for identifier 70 // Check for identifier
76 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) 71 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
77 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); 72 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
78 return recurse(node, index); 73 return recurse(node, index);
79 } 74 }
80 75
@@ -83,13 +78,17 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
83 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 78 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
84 if (ref != null) { 79 if (ref != null) {
85 // make sure this is actually a field 80 // make sure this is actually a field
86 if (ref.getErasedSignature().indexOf('(') >= 0) { 81 String erasedSignature = ref.getErasedSignature();
82 if (erasedSignature.indexOf('(') >= 0) {
87 throw new Error("Expected a field here! got " + ref); 83 throw new Error("Expected a field here! got " + ref);
88 } 84 }
89 85
90 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); 86 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
91 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 87 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
92 index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); 88 if (fieldEntry == null) {
89 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName());
90 }
91 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
93 } 92 }
94 93
95 return recurse(node, index); 94 return recurse(node, index);
@@ -99,8 +98,8 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
99 public Void visitSimpleType(SimpleType node, SourceIndex index) { 98 public Void visitSimpleType(SimpleType node, SourceIndex index) {
100 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 99 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
101 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 100 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
102 ClassEntry classEntry = new ClassEntry(ref.getInternalName()); 101 ClassEntry classEntry = entryPool.getClass(ref.getInternalName());
103 index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); 102 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry);
104 } 103 }
105 104
106 return recurse(node, index); 105 return recurse(node, index);
@@ -109,13 +108,16 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
109 @Override 108 @Override
110 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 109 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
111 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 110 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
112 if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) { 111
113 ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), 112 int variableOffset = this.methodEntry.getVariableOffset(ownerEntry);
114 argumentPosition++, node.getName()); 113 int parameterIndex = def.getSlot() - variableOffset;
114
115 if (parameterIndex >= 0) {
116 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, parameterIndex, 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
@@ -167,21 +172,6 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
167 } 172 }
168 173
169 @Override 174 @Override
170 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
171 if (node.getVariableType() instanceof SimpleType) {
172 SimpleType type = (SimpleType) node.getVariableType();
173 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
174 Identifier identifier = node.getVariableNameToken();
175 String signature = Descriptor.of(typeReference.getErasedDescription());
176 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, identifier.getName(), new Type(signature));
177 identifierEntryCache.put(identifier.getName(), localVariableEntry);
178 addDeclarationToUnmatched(identifier.getName(), index);
179 index.addDeclaration(identifier, localVariableEntry);
180 }
181 return recurse(node, index);
182 }
183
184 @Override
185 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { 175 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
186 AstNodeCollection<VariableInitializer> variables = node.getVariables(); 176 AstNodeCollection<VariableInitializer> variables = node.getVariables();
187 177
@@ -189,16 +179,46 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
189 if (variables.size() == 1) { 179 if (variables.size() == 1) {
190 VariableInitializer initializer = variables.firstOrNullObject(); 180 VariableInitializer initializer = variables.firstOrNullObject();
191 if (initializer != null && node.getType() instanceof SimpleType) { 181 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(); 182 Identifier identifier = initializer.getNameToken();
196 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, argumentPosition + localsPosition++, initializer.getName(), new Type(signature)); 183 Variable variable = initializer.getUserData(Keys.VARIABLE);
197 identifierEntryCache.put(identifier.getName(), localVariableEntry); 184 if (variable != null) {
198 addDeclarationToUnmatched(identifier.getName(), index); 185 VariableDefinition originalVariable = variable.getOriginalVariable();
199 index.addDeclaration(identifier, localVariableEntry); 186 if (originalVariable != null) {
187 int variableOffset = methodEntry.getVariableOffset(ownerEntry);
188 int variableIndex = originalVariable.getSlot() - variableOffset;
189 if (variableIndex >= 0) {
190 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, variableIndex, initializer.getName());
191 identifierEntryCache.put(identifier.getName(), localVariableEntry);
192 addDeclarationToUnmatched(identifier.getName(), index);
193 index.addDeclaration(identifier, localVariableEntry);
194 }
195 }
196 }
200 } 197 }
201 } 198 }
202 return recurse(node, index); 199 return recurse(node, index);
203 } 200 }
201
202 @Override
203 public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) {
204 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
205
206 if (ref instanceof MethodReference) {
207 // get the behavior entry
208 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
209 MethodEntry methodEntry = null;
210
211 methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature());
212 // get the node for the token
213 AstNode tokenNode = node.getMethodNameToken();
214 if (tokenNode == null || (tokenNode.getRegion().getBeginLine() == 0 || tokenNode.getRegion().getEndLine() == 0)){
215 tokenNode = node.getTarget();
216 }
217 if (tokenNode != null) {
218 index.addReference(tokenNode, methodEntry, this.methodEntry);
219 }
220 }
221
222 return recurse(node, index);
223 }
204} 224}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index a94a55b7..e588d24b 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -14,17 +14,25 @@ package cuchaz.enigma.analysis;
14import com.strobel.assembler.metadata.TypeDefinition; 14import 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.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.Signature;
19import cuchaz.enigma.mapping.entry.ClassDefEntry;
20import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
18 21
19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 22public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
23 private final ReferencedEntryPool entryPool;
24
25 public SourceIndexVisitor(ReferencedEntryPool entryPool) {
26 this.entryPool = entryPool;
27 }
20 28
21 @Override 29 @Override
22 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 30 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
23 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 31 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
24 ClassEntry classEntry = new ClassEntry(def.getInternalName()); 32 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
25 index.addDeclaration(node.getNameToken(), classEntry); 33 index.addDeclaration(node.getNameToken(), classEntry);
26 34
27 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); 35 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
28 } 36 }
29 37
30 protected Void recurse(AstNode node, SourceIndex index) { 38 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..b2ddc5fa 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -15,11 +15,9 @@ 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 cuchaz.enigma.mapping.entry.*;
20import javassist.CtClass;
21import javassist.CtField;
22import javassist.bytecode.Descriptor;
23 21
24import java.util.Collection; 22import java.util.Collection;
25import java.util.List; 23import java.util.List;
@@ -28,96 +26,91 @@ import java.util.Set;
28 26
29public class TranslationIndex { 27public class TranslationIndex {
30 28
29 private final ReferencedEntryPool entryPool;
31 private Map<ClassEntry, ClassEntry> superclasses; 30 private Map<ClassEntry, ClassEntry> superclasses;
32 private Multimap<ClassEntry, FieldEntry> fieldEntries; 31 private Multimap<ClassEntry, FieldDefEntry> fieldEntries;
33 private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; 32 private Multimap<ClassEntry, MethodDefEntry> methodEntries;
34 private Multimap<ClassEntry, ClassEntry> interfaces; 33 private Multimap<ClassEntry, ClassEntry> interfaces;
35 34
36 public TranslationIndex() { 35 public TranslationIndex(ReferencedEntryPool entryPool) {
36 this.entryPool = entryPool;
37 this.superclasses = Maps.newHashMap(); 37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create(); 38 this.fieldEntries = HashMultimap.create();
39 this.behaviorEntries = HashMultimap.create(); 39 this.methodEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create(); 40 this.interfaces = HashMultimap.create();
41 } 41 }
42 42
43 public TranslationIndex(TranslationIndex other, Translator translator) { 43 public TranslationIndex(TranslationIndex other, Translator translator) {
44 this.entryPool = other.entryPool;
45
44 // translate the superclasses 46 // translate the superclasses
45 this.superclasses = Maps.newHashMap(); 47 this.superclasses = Maps.newHashMap();
46 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { 48 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
47 this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); 49 this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue()));
48 } 50 }
49 51
50 // translate the interfaces 52 // translate the interfaces
51 this.interfaces = HashMultimap.create(); 53 this.interfaces = HashMultimap.create();
52 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { 54 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
53 this.interfaces.put( 55 this.interfaces.put(
54 translator.translateEntry(mapEntry.getKey()), 56 translator.getTranslatedClass(mapEntry.getKey()),
55 translator.translateEntry(mapEntry.getValue()) 57 translator.getTranslatedClass(mapEntry.getValue())
56 ); 58 );
57 } 59 }
58 60
59 // translate the fields 61 // translate the fields
60 this.fieldEntries = HashMultimap.create(); 62 this.fieldEntries = HashMultimap.create();
61 for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { 63 for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) {
62 this.fieldEntries.put( 64 this.fieldEntries.put(
63 translator.translateEntry(mapEntry.getKey()), 65 translator.getTranslatedClass(mapEntry.getKey()),
64 translator.translateEntry(mapEntry.getValue()) 66 translator.getTranslatedFieldDef(mapEntry.getValue())
65 ); 67 );
66 } 68 }
67 69
68 this.behaviorEntries = HashMultimap.create(); 70 this.methodEntries = HashMultimap.create();
69 for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { 71 for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) {
70 this.behaviorEntries.put( 72 this.methodEntries.put(
71 translator.translateEntry(mapEntry.getKey()), 73 translator.getTranslatedClass(mapEntry.getKey()),
72 translator.translateEntry(mapEntry.getValue()) 74 translator.getTranslatedMethodDef(mapEntry.getValue())
73 ); 75 );
74 } 76 }
75 } 77 }
76 78
77 public void indexClass(CtClass c) { 79 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
78 indexClass(c, true); 80 ClassDefEntry classEntry = new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access));
79 }
80
81 public void indexClass(CtClass c, boolean indexMembers) {
82 ClassEntry classEntry = EntryFactory.getClassEntry(c);
83 if (isJre(classEntry)) { 81 if (isJre(classEntry)) {
84 return; 82 return null;
85 } 83 }
86 84
87 // add the superclass 85 // add the superclass
88 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); 86 ClassEntry superclassEntry = entryPool.getClass(superName);
89 if (superclassEntry != null) { 87 if (superclassEntry != null) {
90 this.superclasses.put(classEntry, superclassEntry); 88 this.superclasses.put(classEntry, superclassEntry);
91 } 89 }
92 90
93 // add the interfaces 91 // add the interfaces
94 for (String interfaceClassName : c.getClassFile().getInterfaces()) { 92 for (String interfaceClassName : interfaces) {
95 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); 93 ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName);
96 if (!isJre(interfaceClassEntry)) { 94 if (!isJre(interfaceClassEntry)) {
97
98 this.interfaces.put(classEntry, interfaceClassEntry); 95 this.interfaces.put(classEntry, interfaceClassEntry);
99 } 96 }
100 } 97 }
101 98
102 if (indexMembers) { 99 return classEntry;
103 // add fields 100 }
104 for (CtField field : c.getDeclaredFields()) {
105 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
106 this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
107 }
108 101
109 // add behaviors 102 protected void indexField(FieldDefEntry fieldEntry) {
110 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 103 this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
111 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); 104 }
112 this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); 105
113 } 106 protected void indexMethod(MethodDefEntry methodEntry) {
114 } 107 this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry);
115 } 108 }
116 109
117 public void renameClasses(Map<String, String> renames) { 110 public void renameClasses(Map<String, String> renames) {
118 EntryRenamer.renameClassesInMap(renames, this.superclasses); 111 EntryRenamer.renameClassesInMap(renames, this.superclasses);
119 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); 112 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
120 EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); 113 EntryRenamer.renameClassesInMultimap(renames, this.methodEntries);
121 } 114 }
122 115
123 public ClassEntry getSuperclass(ClassEntry classEntry) { 116 public ClassEntry getSuperclass(ClassEntry classEntry) {
@@ -175,31 +168,32 @@ public class TranslationIndex {
175 } 168 }
176 169
177 public boolean entryExists(Entry entry) { 170 public boolean entryExists(Entry entry) {
171 if (entry == null) {
172 return false;
173 }
178 if (entry instanceof FieldEntry) { 174 if (entry instanceof FieldEntry) {
179 return fieldExists((FieldEntry) entry); 175 return fieldExists((FieldEntry) entry);
180 } else if (entry instanceof BehaviorEntry) { 176 } else if (entry instanceof MethodEntry) {
181 return behaviorExists((BehaviorEntry) entry); 177 return methodExists((MethodEntry) entry);
182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) { 178 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); 179 return methodExists(((LocalVariableEntry) entry).getOwnerEntry());
186 } 180 }
187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 181 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
188 } 182 }
189 183
190 public boolean fieldExists(FieldEntry fieldEntry) { 184 public boolean fieldExists(FieldEntry fieldEntry) {
191 return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); 185 return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry);
192 } 186 }
193 187
194 public boolean behaviorExists(BehaviorEntry behaviorEntry) { 188 public boolean methodExists(MethodEntry methodEntry) {
195 return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); 189 return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry);
196 } 190 }
197 191
198 public ClassEntry resolveEntryClass(Entry entry) { 192 public ClassEntry resolveEntryOwner(Entry entry) {
199 return resolveEntryClass(entry, false); 193 return resolveEntryOwner(entry, false);
200 } 194 }
201 195
202 public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { 196 public ClassEntry resolveEntryOwner(Entry entry, boolean checkSuperclassBeforeChild) {
203 if (entry instanceof ClassEntry) { 197 if (entry instanceof ClassEntry) {
204 return (ClassEntry) entry; 198 return (ClassEntry) entry;
205 } 199 }
@@ -227,12 +221,12 @@ public class TranslationIndex {
227 Entry originalEntry = entry; 221 Entry originalEntry = entry;
228 222
229 // Get all possible superclasses and reverse the list 223 // Get all possible superclasses and reverse the list
230 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); 224 List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getOwnerClassEntry()));
231 225
232 boolean existInEntry = false; 226 boolean existInEntry = false;
233 227
234 for (ClassEntry classEntry : superclasses) { 228 for (ClassEntry classEntry : superclasses) {
235 entry = entry.cloneToNewClass(classEntry); 229 entry = entry.updateOwnership(classEntry);
236 existInEntry = entryExists(entry); 230 existInEntry = entryExists(entry);
237 231
238 // Check for possible entry in interfaces of superclasses 232 // Check for possible entry in interfaces of superclasses
@@ -245,9 +239,9 @@ public class TranslationIndex {
245 239
246 // Doesn't exists in superclasses? check the child or return null 240 // Doesn't exists in superclasses? check the child or return null
247 if (!existInEntry) 241 if (!existInEntry)
248 return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); 242 return !entryExists(originalEntry) ? null : originalEntry.getOwnerClassEntry();
249 243
250 return entry.getClassEntry(); 244 return entry.getOwnerClassEntry();
251 } 245 }
252 246
253 public ClassEntry resolveSuperclass(Entry entry) { 247 public ClassEntry resolveSuperclass(Entry entry) {
@@ -256,7 +250,7 @@ public class TranslationIndex {
256 250
257 while (!entryExists(entry)) { 251 while (!entryExists(entry)) {
258 // is there a parent class? 252 // is there a parent class?
259 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); 253 ClassEntry superclassEntry = getSuperclass(entry.getOwnerClassEntry());
260 if (superclassEntry == null) { 254 if (superclassEntry == null) {
261 // this is probably a method from a class in a library 255 // 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 256 // we can't trace the implementation up any higher unless we index the library
@@ -264,23 +258,23 @@ public class TranslationIndex {
264 } 258 }
265 259
266 // move up to the parent class 260 // move up to the parent class
267 entry = entry.cloneToNewClass(superclassEntry); 261 entry = entry.updateOwnership(superclassEntry);
268 } 262 }
269 return entry.getClassEntry(); 263 return entry.getOwnerClassEntry();
270 } 264 }
271 265
272 public ClassEntry resolveInterface(Entry entry) { 266 public ClassEntry resolveInterface(Entry entry) {
273 // the interfaces for any class is a forest 267 // the interfaces for any class is a forest
274 // so let's look at all the trees 268 // so let's look at all the trees
275 269
276 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { 270 for (ClassEntry interfaceEntry : this.interfaces.get(entry.getOwnerClassEntry())) {
277 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); 271 Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry);
278 if (subInterface != null && !subInterface.isEmpty()) { 272 if (subInterface != null && !subInterface.isEmpty()) {
279 ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); 273 ClassEntry result = resolveInterface(entry.updateOwnership(interfaceEntry));
280 if (result != null) 274 if (result != null)
281 return result; 275 return result;
282 } 276 }
283 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); 277 ClassEntry resolvedClassEntry = resolveSuperclass(entry.updateOwnership(interfaceEntry));
284 if (resolvedClassEntry != null) { 278 if (resolvedClassEntry != null) {
285 return resolvedClassEntry; 279 return resolvedClassEntry;
286 } 280 }
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..21b24897
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
@@ -0,0 +1,80 @@
1package cuchaz.enigma.bytecode;
2
3import org.objectweb.asm.Opcodes;
4
5import java.lang.reflect.Modifier;
6
7public class AccessFlags {
8 private int flags;
9
10 public AccessFlags(int flags) {
11 this.flags = flags;
12 }
13
14 public boolean isPrivate() {
15 return Modifier.isPrivate(this.flags);
16 }
17
18 public boolean isProtected() {
19 return Modifier.isProtected(this.flags);
20 }
21
22 public boolean isPublic() {
23 return Modifier.isPublic(this.flags);
24 }
25
26 public boolean isSynthetic() {
27 return (this.flags & Opcodes.ACC_SYNTHETIC) != 0;
28 }
29
30 public boolean isStatic() {
31 return Modifier.isStatic(this.flags);
32 }
33
34 public boolean isEnum() {
35 return (flags & Opcodes.ACC_ENUM) != 0;
36 }
37
38 public AccessFlags setPrivate() {
39 this.setVisibility(Opcodes.ACC_PRIVATE);
40 return this;
41 }
42
43 public AccessFlags setProtected() {
44 this.setVisibility(Opcodes.ACC_PROTECTED);
45 return this;
46 }
47
48 public AccessFlags setPublic() {
49 this.setVisibility(Opcodes.ACC_PUBLIC);
50 return this;
51 }
52
53 public AccessFlags setBridged() {
54 this.setVisibility(Opcodes.ACC_BRIDGE);
55 return this;
56 }
57
58 public void setVisibility(int visibility) {
59 this.resetVisibility();
60 this.flags |= visibility;
61 }
62
63 private void resetVisibility() {
64 this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
65 }
66
67 public int getFlags() {
68 return this.flags;
69 }
70
71 @Override
72 public boolean equals(Object obj) {
73 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags;
74 }
75
76 @Override
77 public int hashCode() {
78 return flags;
79 }
80}
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 57d60fdb..00000000
--- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java
+++ /dev/null
@@ -1,85 +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 }
39 }
40
41 // add the attribute to the method
42 info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices));
43 }
44
45 private static byte[] writeStruct(List<Integer> parameterNameIndices) {
46 // JVM 8 Spec says the struct looks like this:
47 // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24
48 // uint8 num_params
49 // for each param:
50 // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
51 // uint16 access_flags -> don't care, just set to 0
52
53 ByteArrayOutputStream buf = new ByteArrayOutputStream();
54 DataOutputStream out = new DataOutputStream(buf);
55
56 // NOTE: java hates unsigned integers, so we have to be careful here
57 // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
58 // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
59 // if the int is out of range, the byte stream won't look the way we want and weird things will happen
60 final int SIZEOF_UINT8 = 1;
61 final int SIZEOF_UINT16 = 2;
62 final int MAX_UINT8 = (1 << 8) - 1;
63 final int MAX_UINT16 = (1 << 16) - 1;
64
65 try {
66 assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8);
67 out.writeByte(parameterNameIndices.size());
68
69 for (Integer index : parameterNameIndices) {
70 assert (index >= 0 && index <= MAX_UINT16);
71 out.writeShort(index);
72
73 // just write 0 for the access flags
74 out.writeShort(0);
75 }
76
77 out.close();
78 byte[] data = buf.toByteArray();
79 assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16));
80 return data;
81 } catch (IOException ex) {
82 throw new Error(ex);
83 }
84 }
85}
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/TranslationAnnotationVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
new file mode 100644
index 00000000..df5f8f7e
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
@@ -0,0 +1,43 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.ClassEntry;
6import cuchaz.enigma.mapping.entry.FieldEntry;
7import org.objectweb.asm.AnnotationVisitor;
8
9public class TranslationAnnotationVisitor extends AnnotationVisitor {
10 private final Translator translator;
11 private final ClassEntry annotationEntry;
12
13 public TranslationAnnotationVisitor(Translator translator, ClassEntry annotationEntry, int api, AnnotationVisitor av) {
14 super(api, av);
15 this.translator = translator;
16 this.annotationEntry = annotationEntry;
17 }
18
19 @Override
20 public void visit(String name, Object value) {
21 super.visit(name, translator.getTranslatedValue(value));
22 }
23
24 @Override
25 public AnnotationVisitor visitArray(String name) {
26 return this;
27 }
28
29 @Override
30 public AnnotationVisitor visitAnnotation(String name, String desc) {
31 TypeDescriptor type = new TypeDescriptor(desc);
32 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type));
33 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString());
34 }
35
36 @Override
37 public void visitEnum(String name, String desc, String value) {
38 TypeDescriptor type = new TypeDescriptor(desc);
39 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type));
40 FieldEntry enumField = translator.getTranslatedField(new FieldEntry(type.getTypeEntry(), value, type));
41 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName());
42 }
43}
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..234d11f3
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.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.bytecode.translators;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.MethodDescriptor;
17import cuchaz.enigma.mapping.Signature;
18import cuchaz.enigma.mapping.Translator;
19import cuchaz.enigma.mapping.TypeDescriptor;
20import cuchaz.enigma.mapping.entry.*;
21import org.objectweb.asm.*;
22
23public class TranslationClassVisitor extends ClassVisitor {
24 private final Translator translator;
25 private final JarIndex jarIndex;
26 private final ReferencedEntryPool entryPool;
27
28 private ClassDefEntry obfClassEntry;
29 private Signature obfSignature;
30
31 public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) {
32 super(api, cv);
33 this.translator = translator;
34 this.jarIndex = jarIndex;
35 this.entryPool = entryPool;
36 }
37
38 @Override
39 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
40 obfSignature = Signature.createSignature(signature);
41 obfClassEntry = new ClassDefEntry(name, obfSignature, new AccessFlags(access));
42 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(obfClassEntry);
43 ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName));
44 String[] translatedInterfaces = new String[interfaces.length];
45 for (int i = 0; i < interfaces.length; i++) {
46 translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName();
47 }
48 super.visit(version, translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getSignature().toString(), superEntry.getName(), translatedInterfaces);
49 }
50
51 @Override
52 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
53 FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
54 FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry);
55 FieldVisitor fv = super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), value);
56 return new TranslationFieldVisitor(translator, translatedEntry, api, fv);
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), Signature.createSignature(signature), new AccessFlags(access));
62 MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry);
63 if (jarIndex.getBridgedMethod(entry) != null) {
64 translatedEntry.getAccess().setBridged();
65 }
66 String[] translatedExceptions = new String[exceptions.length];
67 for (int i = 0; i < exceptions.length; i++) {
68 translatedExceptions[i] = translator.getTranslatedClass(entryPool.getClass(exceptions[i])).getName();
69 }
70 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions);
71 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv);
72 }
73
74 @Override
75 public void visitInnerClass(String name, String outerName, String innerName, int access) {
76 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(name, obfSignature, new AccessFlags(access)));
77 String translatedName = translatedEntry.getName();
78 int separatorIndex = translatedName.lastIndexOf("$");
79 String parentName = translatedName.substring(0, separatorIndex);
80 String childName = translatedName.substring(separatorIndex + 1);
81
82 ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(parentName));
83
84 // Anonymous classes do not specify an outer or inner name. As we do not translate from the given parameter, ignore if the input is null
85 String translatedOuterName = outerName != null ? outerEntry.getName() : null;
86 String translatedInnerName = innerName != null ? childName : null;
87 super.visitInnerClass(translatedName, translatedOuterName, translatedInnerName, translatedEntry.getAccess().getFlags());
88 }
89
90 @Override
91 public void visitOuterClass(String owner, String name, String desc) {
92 if (desc != null) {
93 MethodEntry translatedEntry = translator.getTranslatedMethod(new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)));
94 super.visitOuterClass(translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
95 } else {
96 super.visitOuterClass(owner, name, desc);
97 }
98 }
99
100 @Override
101 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
102 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
103 AnnotationVisitor av = super.visitAnnotation(translatedDesc.toString(), visible);
104 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
105 }
106
107 @Override
108 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
109 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
110 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible);
111 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
new file mode 100644
index 00000000..e4695fb6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
@@ -0,0 +1,33 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.FieldDefEntry;
6import org.objectweb.asm.AnnotationVisitor;
7import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.TypePath;
9
10public class TranslationFieldVisitor extends FieldVisitor {
11 private final FieldDefEntry fieldEntry;
12 private final Translator translator;
13
14 public TranslationFieldVisitor(Translator translator, FieldDefEntry fieldEntry, int api, FieldVisitor fv) {
15 super(api, fv);
16 this.translator = translator;
17 this.fieldEntry = fieldEntry;
18 }
19
20 @Override
21 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
22 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
23 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
24 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
25 }
26
27 @Override
28 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
29 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
30 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
31 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
32 }
33}
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..0141b45e
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -0,0 +1,191 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.mapping.MethodDescriptor;
4import cuchaz.enigma.mapping.Signature;
5import cuchaz.enigma.mapping.Translator;
6import cuchaz.enigma.mapping.TypeDescriptor;
7import cuchaz.enigma.mapping.entry.*;
8import org.objectweb.asm.*;
9
10import java.util.List;
11import java.util.Locale;
12
13public class TranslationMethodVisitor extends MethodVisitor {
14 private final ClassDefEntry ownerEntry;
15 private final MethodDefEntry methodEntry;
16 private final Translator translator;
17
18 private boolean hasParameterMeta;
19
20 public TranslationMethodVisitor(Translator translator, ClassDefEntry ownerEntry, MethodDefEntry methodEntry, int api, MethodVisitor mv) {
21 super(api, mv);
22 this.translator = translator;
23 this.ownerEntry = ownerEntry;
24 this.methodEntry = methodEntry;
25 }
26
27 @Override
28 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
29 FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc));
30 FieldEntry translatedEntry = translator.getTranslatedField(entry);
31 super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
32 }
33
34 @Override
35 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
36 MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc));
37 MethodEntry translatedEntry = translator.getTranslatedMethod(entry);
38 super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf);
39 }
40
41 @Override
42 public void visitFrame(int type, int localCount, Object[] locals, int stackCount, Object[] stack) {
43 Object[] translatedLocals = this.getTranslatedFrame(locals, localCount);
44 Object[] translatedStack = this.getTranslatedFrame(stack, stackCount);
45 super.visitFrame(type, localCount, translatedLocals, stackCount, translatedStack);
46 }
47
48 private Object[] getTranslatedFrame(Object[] array, int count) {
49 if (array == null) {
50 return null;
51 }
52 for (int i = 0; i < count; i++) {
53 Object object = array[i];
54 if (object instanceof String) {
55 String type = (String) object;
56 array[i] = translator.getTranslatedClass(new ClassEntry(type)).getName();
57 }
58 }
59 return array;
60 }
61
62 @Override
63 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
64 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
65 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
66 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
67 }
68
69 @Override
70 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
71 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
72 AnnotationVisitor av = super.visitParameterAnnotation(parameter, typeDesc.toString(), visible);
73 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
74 }
75
76 @Override
77 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
78 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
79 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, typeDesc.toString(), visible);
80 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
81 }
82
83 @Override
84 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
85 hasParameterMeta = true;
86
87 String translatedSignature = translator.getTranslatedSignature(Signature.createTypedSignature(signature)).toString();
88
89 int offset = methodEntry.getVariableOffset(ownerEntry);
90
91 int offsetIndex = index - offset;
92 if (offsetIndex >= 0) {
93 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, offsetIndex, name, new TypeDescriptor(desc));
94 LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry);
95 String translatedName = translatedEntry.getName();
96
97 // TODO: Better name inference
98 if (translatedName.equals(entry.getName())) {
99 boolean argument = offsetIndex < methodEntry.getDesc().getArgumentDescs().size();
100 translatedName = inferName(argument, offsetIndex, translatedEntry.getDesc());
101 }
102
103 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index);
104 } else {
105 // Handle "this" variable
106 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc));
107 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index);
108 }
109 }
110
111 @Override
112 public void visitTypeInsn(int opcode, String type) {
113 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type));
114 super.visitTypeInsn(opcode, translatedEntry.getName());
115 }
116
117 @Override
118 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
119 MethodDescriptor translatedMethodDesc = translator.getTranslatedMethodDesc(new MethodDescriptor(desc));
120 Object[] translatedBsmArgs = new Object[bsmArgs.length];
121 for (int i = 0; i < bsmArgs.length; i++) {
122 translatedBsmArgs[i] = translator.getTranslatedValue(bsmArgs[i]);
123 }
124 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), translator.getTranslatedHandle(bsm), translatedBsmArgs);
125 }
126
127 @Override
128 public void visitLdcInsn(Object cst) {
129 super.visitLdcInsn(translator.getTranslatedValue(cst));
130 }
131
132 @Override
133 public void visitMultiANewArrayInsn(String desc, int dims) {
134 super.visitMultiANewArrayInsn(translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString(), dims);
135 }
136
137 @Override
138 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
139 if (type != null) {
140 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type));
141 super.visitTryCatchBlock(start, end, handler, translatedEntry.getName());
142 } else {
143 super.visitTryCatchBlock(start, end, handler, type);
144 }
145 }
146
147 @Override
148 public void visitEnd() {
149 // If we didn't receive any parameter metadata, generate it
150 if (!hasParameterMeta) {
151 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
152 for (int index = 0; index < arguments.size(); index++) {
153 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "");
154 LocalVariableEntry translatedEntry = translator.getTranslatedVariable(entry);
155 String translatedName = translatedEntry.getName();
156 if (translatedName.equals(entry.getName())) {
157 super.visitParameter(inferName(true, index, arguments.get(index)), 0);
158 } else {
159 super.visitParameter(translatedName, 0);
160 }
161 }
162 }
163 super.visitEnd();
164 }
165
166 private String inferName(boolean argument, int argumentIndex, TypeDescriptor desc) {
167 String translatedName;
168 int nameIndex = argumentIndex + 1;
169 StringBuilder nameBuilder = new StringBuilder(argument ? "a" : "v");
170 // Unfortunately each of these have different name getters, so they have different code paths
171 if (desc.isPrimitive()) {
172 TypeDescriptor.Primitive argCls = desc.getPrimitive();
173 nameBuilder.append(argCls.name());
174 } else if (desc.isArray()) {
175 // List types would require this whole block again, so just go with aListx
176 nameBuilder.append("Arr");
177 } else if (desc.isType()) {
178 String typeName = desc.getTypeEntry().getSimpleName().replace("$", "");
179 typeName = typeName.substring(0, 1).toUpperCase(Locale.ROOT) + typeName.substring(1);
180 nameBuilder.append(typeName);
181 } else {
182 System.err.println("Encountered invalid argument type descriptor " + desc.toString());
183 nameBuilder.append("Unk");
184 }
185 if (!argument || methodEntry.getDesc().getArgumentDescs().size() > 1) {
186 nameBuilder.append(nameIndex);
187 }
188 translatedName = nameBuilder.toString();
189 return translatedName;
190 }
191}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
new file mode 100644
index 00000000..e66b085f
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java
@@ -0,0 +1,129 @@
1package cuchaz.enigma.bytecode.translators;
2
3import org.objectweb.asm.Opcodes;
4import org.objectweb.asm.signature.SignatureVisitor;
5
6import java.util.Stack;
7import java.util.function.Function;
8
9public class TranslationSignatureVisitor extends SignatureVisitor {
10 private final Function<String, String> remapper;
11
12 private final SignatureVisitor sv;
13 private final Stack<String> classStack = new Stack<>();
14
15 public TranslationSignatureVisitor(Function<String, String> remapper, SignatureVisitor sv) {
16 super(Opcodes.ASM5);
17 this.remapper = remapper;
18 this.sv = sv;
19 }
20
21 @Override
22 public void visitClassType(String name) {
23 classStack.push(name);
24 String translatedEntry = this.remapper.apply(name);
25 this.sv.visitClassType(translatedEntry);
26 }
27
28 @Override
29 public void visitInnerClassType(String name) {
30 String lastClass = classStack.pop();
31 if (!name.startsWith(lastClass+"$")){//todo see if there's a way to base this on whether there were type params or not
32 name = lastClass+"$"+name;
33 }
34 String translatedEntry = this.remapper.apply(name);
35 if (translatedEntry.contains("/")){
36 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/")+1);
37 }
38 if (translatedEntry.contains("$")){
39 translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$")+1);
40 }
41 this.sv.visitInnerClassType(translatedEntry);
42 }
43
44 @Override
45 public void visitFormalTypeParameter(String name) {
46 this.sv.visitFormalTypeParameter(name);
47 }
48
49 @Override
50 public void visitTypeVariable(String name) {
51 this.sv.visitTypeVariable(name);
52 }
53
54 @Override
55 public SignatureVisitor visitArrayType() {
56 this.sv.visitArrayType();
57 return this;
58 }
59
60 @Override
61 public void visitBaseType(char descriptor) {
62 this.sv.visitBaseType(descriptor);
63 }
64
65 @Override
66 public SignatureVisitor visitClassBound() {
67 this.sv.visitClassBound();
68 return this;
69 }
70
71 @Override
72 public SignatureVisitor visitExceptionType() {
73 this.sv.visitExceptionType();
74 return this;
75 }
76
77 @Override
78 public SignatureVisitor visitInterface() {
79 this.sv.visitInterface();
80 return this;
81 }
82
83 @Override
84 public SignatureVisitor visitInterfaceBound() {
85 this.sv.visitInterfaceBound();
86 return this;
87 }
88
89 @Override
90 public SignatureVisitor visitParameterType() {
91 this.sv.visitParameterType();
92 return this;
93 }
94
95 @Override
96 public SignatureVisitor visitReturnType() {
97 this.sv.visitReturnType();
98 return this;
99 }
100
101 @Override
102 public SignatureVisitor visitSuperclass() {
103 this.sv.visitSuperclass();
104 return this;
105 }
106
107 @Override
108 public void visitTypeArgument() {
109 this.sv.visitTypeArgument();
110 }
111
112 @Override
113 public SignatureVisitor visitTypeArgument(char wildcard) {
114 this.sv.visitTypeArgument(wildcard);
115 return this;
116 }
117
118 @Override
119 public void visitEnd() {
120 this.sv.visitEnd();
121 if (!classStack.empty())
122 classStack.pop();
123 }
124
125 @Override
126 public String toString() {
127 return this.sv.toString();
128 }
129}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index ed84ef24..93c5d4ba 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -17,7 +17,7 @@ import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
18import cuchaz.enigma.gui.node.ClassSelectorClassNode; 18import cuchaz.enigma.gui.node.ClassSelectorClassNode;
19import cuchaz.enigma.gui.node.ClassSelectorPackageNode; 19import cuchaz.enigma.gui.node.ClassSelectorPackageNode;
20import cuchaz.enigma.mapping.ClassEntry; 20import cuchaz.enigma.mapping.entry.ClassEntry;
21import cuchaz.enigma.throwables.IllegalNameException; 21import cuchaz.enigma.throwables.IllegalNameException;
22 22
23import javax.swing.*; 23import javax.swing.*;
@@ -328,8 +328,12 @@ public class ClassSelector extends JTree {
328 } 328 }
329 329
330 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { 330 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) {
331 String packageName = entry.getPackageName();
332 if (packageName == null){
333 packageName = "(none)";
334 }
331 for (ClassSelectorPackageNode packageNode : packageNodes()) { 335 for (ClassSelectorPackageNode packageNode : packageNodes()) {
332 if (packageNode.getPackageName().equals(entry.getPackageName())) { 336 if (packageNode.getPackageName().equals(packageName)) {
333 return packageNode; 337 return packageNode;
334 } 338 }
335 } 339 }
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
index f76dc897..ac45b4a7 100644
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java
@@ -17,8 +17,8 @@ import cuchaz.enigma.analysis.EntryReference;
17import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.analysis.Token; 18import cuchaz.enigma.analysis.Token;
19import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; 19import cuchaz.enigma.gui.highlight.SelectionHighlightPainter;
20import cuchaz.enigma.mapping.ClassEntry; 20import cuchaz.enigma.mapping.entry.ClassEntry;
21import cuchaz.enigma.mapping.Entry; 21import cuchaz.enigma.mapping.entry.Entry;
22import de.sciss.syntaxpane.DefaultSyntaxKit; 22import de.sciss.syntaxpane.DefaultSyntaxKit;
23 23
24import javax.swing.*; 24import javax.swing.*;
@@ -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..cfac8ad8 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -32,10 +32,10 @@ import cuchaz.enigma.gui.panels.PanelEditor;
32import cuchaz.enigma.gui.panels.PanelIdentifier; 32import cuchaz.enigma.gui.panels.PanelIdentifier;
33import cuchaz.enigma.gui.panels.PanelObf; 33import cuchaz.enigma.gui.panels.PanelObf;
34import cuchaz.enigma.mapping.*; 34import cuchaz.enigma.mapping.*;
35import cuchaz.enigma.mapping.entry.*;
35import cuchaz.enigma.throwables.IllegalNameException; 36import cuchaz.enigma.throwables.IllegalNameException;
36import cuchaz.enigma.utils.Utils; 37import cuchaz.enigma.utils.Utils;
37import de.sciss.syntaxpane.DefaultSyntaxKit; 38import de.sciss.syntaxpane.DefaultSyntaxKit;
38import javassist.bytecode.Descriptor;
39 39
40import javax.swing.*; 40import javax.swing.*;
41import javax.swing.text.BadLocationException; 41import javax.swing.text.BadLocationException;
@@ -48,8 +48,10 @@ import java.awt.*;
48import java.awt.event.*; 48import java.awt.event.*;
49import java.io.File; 49import java.io.File;
50import java.io.IOException; 50import java.io.IOException;
51import java.util.*; 51import java.util.Collection;
52import java.util.Collections;
52import java.util.List; 53import java.util.List;
54import java.util.Vector;
53import java.util.function.Function; 55import java.util.function.Function;
54 56
55public class Gui { 57public class Gui {
@@ -438,14 +440,10 @@ public class Gui {
438 showFieldEntry((FieldEntry) this.reference.entry); 440 showFieldEntry((FieldEntry) this.reference.entry);
439 } else if (this.reference.entry instanceof MethodEntry) { 441 } else if (this.reference.entry instanceof MethodEntry) {
440 showMethodEntry((MethodEntry) this.reference.entry); 442 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) { 443 } else if (this.reference.entry instanceof LocalVariableEntry) {
446 showLocalVariableEntry((LocalVariableEntry) this.reference.entry); 444 showLocalVariableEntry((LocalVariableEntry) this.reference.entry);
447 } else { 445 } else {
448 throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); 446 throw new Error("Unknown entry desc: " + this.reference.entry.getClass().getName());
449 } 447 }
450 448
451 redraw(); 449 redraw();
@@ -453,10 +451,9 @@ public class Gui {
453 451
454 private void showLocalVariableEntry(LocalVariableEntry entry) { 452 private void showLocalVariableEntry(LocalVariableEntry entry) {
455 addNameValue(infoPanel, "Variable", entry.getName()); 453 addNameValue(infoPanel, "Variable", entry.getName());
456 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 454 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName());
457 addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); 455 addNameValue(infoPanel, "Method", entry.getOwnerEntry().getName());
458 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); 456 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
459 addNameValue(infoPanel, "Type", entry.getType().toString());
460 } 457 }
461 458
462 private void showClassEntry(ClassEntry entry) { 459 private void showClassEntry(ClassEntry entry) {
@@ -466,32 +463,20 @@ public class Gui {
466 463
467 private void showFieldEntry(FieldEntry entry) { 464 private void showFieldEntry(FieldEntry entry) {
468 addNameValue(infoPanel, "Field", entry.getName()); 465 addNameValue(infoPanel, "Field", entry.getName());
469 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 466 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName());
470 addNameValue(infoPanel, "Type", entry.getType().toString()); 467 addNameValue(infoPanel, "TypeDescriptor", entry.getDesc().toString());
471 addModifierComboBox(infoPanel, "Modifier", entry); 468 addModifierComboBox(infoPanel, "Modifier", entry);
472 } 469 }
473 470
474 private void showMethodEntry(MethodEntry entry) { 471 private void showMethodEntry(MethodEntry entry) {
475 addNameValue(infoPanel, "Method", entry.getName()); 472 if (entry.isConstructor()) {
476 addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); 473 addNameValue(infoPanel, "Constructor", entry.getOwnerClassEntry().getName());
477 addNameValue(infoPanel, "Signature", entry.getSignature().toString()); 474 } else {
478 addModifierComboBox(infoPanel, "Modifier", entry); 475 addNameValue(infoPanel, "Method", entry.getName());
479 476 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 } 477 }
488 } 478 addNameValue(infoPanel, "MethodDescriptor", entry.getDesc().toString());
489 479 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 } 480 }
496 481
497 private void addNameValue(JPanel container, String name, String value) { 482 private void addNameValue(JPanel container, String name, String value) {
@@ -532,8 +517,8 @@ public class Gui {
532 reference = this.controller.getDeobfReference(token); 517 reference = this.controller.getDeobfReference(token);
533 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; 518 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry;
534 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; 519 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry;
535 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; 520 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry && !((MethodEntry) reference.entry).isConstructor();
536 boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; 521 boolean isConstructorEntry = isToken && reference.entry instanceof MethodEntry && ((MethodEntry) reference.entry).isConstructor();
537 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); 522 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry);
538 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); 523 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference);
539 524
@@ -710,16 +695,13 @@ public class Gui {
710 if (reference.entry instanceof ClassEntry) { 695 if (reference.entry instanceof ClassEntry) {
711 // look for calls to the default constructor 696 // look for calls to the default constructor
712 // TODO: get a list of all the constructors and find calls to all of them 697 // 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"))); 698 MethodReferenceTreeNode node = this.controller.getMethodReferences(new MethodEntry((ClassEntry) reference.entry, "<init>", new MethodDescriptor("()V")));
714 callsTree.setModel(new DefaultTreeModel(node)); 699 callsTree.setModel(new DefaultTreeModel(node));
715 } else if (reference.entry instanceof FieldEntry) { 700 } else if (reference.entry instanceof FieldEntry) {
716 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); 701 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry);
717 callsTree.setModel(new DefaultTreeModel(node)); 702 callsTree.setModel(new DefaultTreeModel(node));
718 } else if (reference.entry instanceof MethodEntry) { 703 } else if (reference.entry instanceof MethodEntry) {
719 BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); 704 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)); 705 callsTree.setModel(new DefaultTreeModel(node));
724 } 706 }
725 707
@@ -790,7 +772,6 @@ public class Gui {
790 // package rename 772 // package rename
791 if (data instanceof String) { 773 if (data instanceof String) {
792 for (int i = 0; i < node.getChildCount(); i++) { 774 for (int i = 0; i < node.getChildCount(); i++) {
793 data = Descriptor.toJvmName((String) data);
794 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); 775 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
795 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 776 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
796 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 777 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
@@ -807,15 +788,15 @@ public class Gui {
807 } 788 }
808 789
809 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) { 790 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) {
810 String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); 791 String oldEntry = deobfReference.entry.getOwnerClassEntry().getPackageName();
811 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); 792 String newEntry = new ClassEntry(newName).getPackageName();
812 moveClassTree(deobfReference, newName, oldEntry == null, 793 moveClassTree(deobfReference, newName, oldEntry == null,
813 newEntry == null); 794 newEntry == null);
814 } 795 }
815 796
816 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { 797 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) {
817 ClassEntry oldEntry = deobfReference.entry.getClassEntry(); 798 ClassEntry oldEntry = deobfReference.entry.getOwnerClassEntry();
818 ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); 799 ClassEntry newEntry = new ClassEntry(newName);
819 800
820 // Ob -> deob 801 // Ob -> deob
821 if (isOldOb && !isNewOb) { 802 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..ae1b6528 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -18,6 +18,10 @@ import cuchaz.enigma.Deobfuscator;
18import cuchaz.enigma.analysis.*; 18import cuchaz.enigma.analysis.*;
19import cuchaz.enigma.gui.dialog.ProgressDialog; 19import cuchaz.enigma.gui.dialog.ProgressDialog;
20import cuchaz.enigma.mapping.*; 20import cuchaz.enigma.mapping.*;
21import cuchaz.enigma.mapping.entry.ClassEntry;
22import cuchaz.enigma.mapping.entry.Entry;
23import cuchaz.enigma.mapping.entry.FieldEntry;
24import cuchaz.enigma.mapping.entry.MethodEntry;
21import cuchaz.enigma.throwables.MappingParseException; 25import cuchaz.enigma.throwables.MappingParseException;
22import cuchaz.enigma.utils.ReadableToken; 26import cuchaz.enigma.utils.ReadableToken;
23 27
@@ -51,10 +55,10 @@ public class GuiController {
51 return this.isDirty; 55 return this.isDirty;
52 } 56 }
53 57
54 public void openJar(final JarFile jar) { 58 public void openJar(final JarFile jar) throws IOException {
55 this.gui.onStartOpenJar(); 59 this.gui.onStartOpenJar();
56 this.deobfuscator = new Deobfuscator(jar); 60 this.deobfuscator = new Deobfuscator(jar);
57 this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); 61 this.gui.onFinishOpenJar(jar.getName());
58 refreshClasses(); 62 refreshClasses();
59 } 63 }
60 64
@@ -161,24 +165,24 @@ public class GuiController {
161 165
162 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { 166 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
163 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 167 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
164 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 168 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry);
165 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); 169 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry);
166 } 170 }
167 171
168 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { 172 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) {
169 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 173 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry);
170 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); 174 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry);
171 } 175 }
172 176
173 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { 177 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) {
174 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 178 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
175 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 179 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
176 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); 180 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry);
177 } 181 }
178 182
179 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { 183 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) {
180 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 184 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
181 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); 185 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
182 if (rootNodes.isEmpty()) { 186 if (rootNodes.isEmpty()) {
183 return null; 187 return null;
184 } 188 }
@@ -190,14 +194,14 @@ public class GuiController {
190 194
191 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { 195 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) {
192 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); 196 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry);
193 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); 197 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfFieldEntry);
194 rootNode.load(this.deobfuscator.getJarIndex(), true); 198 rootNode.load(this.deobfuscator.getJarIndex(), true);
195 return rootNode; 199 return rootNode;
196 } 200 }
197 201
198 public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { 202 public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry) {
199 BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); 203 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry);
200 BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); 204 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry);
201 rootNode.load(this.deobfuscator.getJarIndex(), true); 205 rootNode.load(this.deobfuscator.getJarIndex(), true);
202 return rootNode; 206 return rootNode;
203 } 207 }
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
index 8bf57d38..92084559 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
@@ -14,7 +14,6 @@ package cuchaz.enigma.gui;
14import javax.swing.*; 14import javax.swing.*;
15import java.awt.*; 15import java.awt.*;
16import java.awt.event.ActionListener; 16import java.awt.event.ActionListener;
17import java.util.Arrays;
18 17
19public class GuiTricks { 18public class GuiTricks {
20 19
@@ -27,7 +26,7 @@ public class GuiTricks {
27 public static void deactivateButton(JButton button) { 26 public static void deactivateButton(JButton button) {
28 button.setEnabled(false); 27 button.setEnabled(false);
29 button.setText(""); 28 button.setText("");
30 for (ActionListener listener : Arrays.asList(button.getActionListeners())) { 29 for (ActionListener listener : button.getActionListeners()) {
31 button.removeActionListener(listener); 30 button.removeActionListener(listener);
32 } 31 }
33 } 32 }
@@ -35,7 +34,7 @@ public class GuiTricks {
35 public static void activateButton(JButton button, String text, ActionListener newListener) { 34 public static void activateButton(JButton button, String text, ActionListener newListener) {
36 button.setText(text); 35 button.setText(text);
37 button.setEnabled(true); 36 button.setEnabled(true);
38 for (ActionListener listener : Arrays.asList(button.getActionListeners())) { 37 for (ActionListener listener : button.getActionListeners()) {
39 button.removeActionListener(listener); 38 button.removeActionListener(listener);
40 } 39 }
41 button.addActionListener(newListener); 40 button.addActionListener(newListener);
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
index 1fd2fa85..34ec26ee 100644
--- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
+++ b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.entry.ClassEntry;
15 15
16public class ScoredClassEntry extends ClassEntry { 16public class ScoredClassEntry extends ClassEntry {
17 17
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index dc933cd5..a965a8fa 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
13 13
14import cuchaz.enigma.mapping.ClassEntry; 14import cuchaz.enigma.mapping.entry.ClassEntry;
15 15
16import javax.swing.tree.DefaultMutableTreeNode; 16import javax.swing.tree.DefaultMutableTreeNode;
17 17
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/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 4bbd32bd..9eb8f8f8 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -2,7 +2,7 @@ package cuchaz.enigma.gui.panels;
2 2
3import cuchaz.enigma.gui.ClassSelector; 3import cuchaz.enigma.gui.ClassSelector;
4import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
5import cuchaz.enigma.mapping.ClassEntry; 5import cuchaz.enigma.mapping.entry.ClassEntry;
6 6
7import javax.swing.*; 7import javax.swing.*;
8import java.awt.*; 8import java.awt.*;
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/BehaviorEntry.java
deleted file mode 100644
index 04b4ebc7..00000000
--- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.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 BehaviorEntry extends Entry {
15 Signature getSignature();
16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index 3935de5f..8f3f2b2b 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -12,6 +12,9 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.entry.FieldEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
15import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
16 19
17import java.util.ArrayList; 20import java.util.ArrayList;
@@ -34,7 +37,6 @@ public class ClassMapping implements Comparable<ClassMapping> {
34 private Map<String, MethodMapping> methodsByDeobf; 37 private Map<String, MethodMapping> methodsByDeobf;
35 private boolean isDirty; 38 private boolean isDirty;
36 private Mappings.EntryModifier modifier; 39 private Mappings.EntryModifier modifier;
37 private boolean deobfInner;
38 40
39 public ClassMapping(String obfFullName) { 41 public ClassMapping(String obfFullName) {
40 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); 42 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
@@ -81,6 +83,10 @@ public class ClassMapping implements Comparable<ClassMapping> {
81 return deobfName; 83 return deobfName;
82 } 84 }
83 85
86 public String getTranslatedName(TranslationDirection direction) {
87 return direction.choose(deobfName, obfFullName);
88 }
89
84 //// INNER CLASSES //////// 90 //// INNER CLASSES ////////
85 91
86 public void setDeobfName(String val) { 92 public void setDeobfName(String val) {
@@ -191,21 +197,21 @@ public class ClassMapping implements Comparable<ClassMapping> {
191 return fieldsByObf.values(); 197 return fieldsByObf.values();
192 } 198 }
193 199
194 public boolean containsObfField(String obfName, Type obfType) { 200 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
195 return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); 201 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
196 } 202 }
197 203
198 public boolean containsDeobfField(String deobfName, Type deobfType) { 204 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
199 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); 205 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
200 } 206 }
201 207
202 public void addFieldMapping(FieldMapping fieldMapping) { 208 public void addFieldMapping(FieldMapping fieldMapping) {
203 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 209 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
204 if (fieldsByObf.containsKey(obfKey)) { 210 if (fieldsByObf.containsKey(obfKey)) {
205 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 211 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
206 } 212 }
207 if (fieldMapping.getDeobfName() != null) { 213 if (fieldMapping.getDeobfName() != null) {
208 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 214 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
209 if (fieldsByDeobf.containsKey(deobfKey)) { 215 if (fieldsByDeobf.containsKey(deobfKey)) {
210 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 216 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
211 } 217 }
@@ -218,63 +224,67 @@ public class ClassMapping implements Comparable<ClassMapping> {
218 } 224 }
219 225
220 public void removeFieldMapping(FieldMapping fieldMapping) { 226 public void removeFieldMapping(FieldMapping fieldMapping) {
221 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; 227 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
222 assert (obfWasRemoved); 228 assert (obfWasRemoved);
223 if (fieldMapping.getDeobfName() != null) { 229 if (fieldMapping.getDeobfName() != null) {
224 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 230 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
225 assert (deobfWasRemoved); 231 assert (deobfWasRemoved);
226 } 232 }
227 this.isDirty = true; 233 this.isDirty = true;
228 } 234 }
229 235
230 public FieldMapping getFieldByObf(String obfName, Type obfType) { 236 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
231 return fieldsByObf.get(getFieldKey(obfName, obfType)); 237 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
232 } 238 }
233 239
234 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { 240 public FieldMapping getFieldByObf(FieldEntry field) {
235 return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 241 return getFieldByObf(field.getName(), field.getDesc());
236 } 242 }
237 243
238 public String getObfFieldName(String deobfName, Type obfType) { 244 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
239 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 245 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
246 }
247
248 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
249 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
240 if (fieldMapping != null) { 250 if (fieldMapping != null) {
241 return fieldMapping.getObfName(); 251 return fieldMapping.getObfName();
242 } 252 }
243 return null; 253 return null;
244 } 254 }
245 255
246 public String getDeobfFieldName(String obfName, Type obfType) { 256 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
247 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 257 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
248 if (fieldMapping != null) { 258 if (fieldMapping != null) {
249 return fieldMapping.getDeobfName(); 259 return fieldMapping.getDeobfName();
250 } 260 }
251 return null; 261 return null;
252 } 262 }
253 263
254 private String getFieldKey(String name, Type type) { 264 private String getFieldKey(String name, TypeDescriptor desc) {
255 if (name == null) { 265 if (name == null) {
256 throw new IllegalArgumentException("name cannot be null!"); 266 throw new IllegalArgumentException("name cannot be null!");
257 } 267 }
258 if (type == null) { 268 if (desc == null) {
259 throw new IllegalArgumentException("type cannot be null!"); 269 throw new IllegalArgumentException("desc cannot be null!");
260 } 270 }
261 return name + ":" + type; 271 return name + ":" + desc;
262 } 272 }
263 273
264 public void setFieldName(String obfName, Type obfType, String deobfName) { 274 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
265 assert (deobfName != null); 275 assert (deobfName != null);
266 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 276 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
267 if (fieldMapping == null) { 277 if (fieldMapping == null) {
268 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); 278 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
269 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 279 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
270 assert (obfWasAdded); 280 assert (obfWasAdded);
271 } else { 281 } else {
272 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; 282 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
273 assert (wasRemoved); 283 assert (wasRemoved);
274 } 284 }
275 fieldMapping.setDeobfName(deobfName); 285 fieldMapping.setDeobfName(deobfName);
276 if (deobfName != null) { 286 if (deobfName != null) {
277 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 287 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
278 assert (wasAdded); 288 assert (wasAdded);
279 } 289 }
280 this.isDirty = true; 290 this.isDirty = true;
@@ -282,13 +292,13 @@ public class ClassMapping implements Comparable<ClassMapping> {
282 292
283 //// METHODS //////// 293 //// METHODS ////////
284 294
285 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 295 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
286 assert (newObfName != null); 296 assert (newObfName != null);
287 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); 297 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
288 assert (fieldMapping != null); 298 assert (fieldMapping != null);
289 fieldMapping.setObfName(newObfName); 299 fieldMapping.setObfName(newObfName);
290 fieldMapping.setObfType(newObfType); 300 fieldMapping.setObfDesc(newObfDesc);
291 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 301 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
292 assert (obfWasAdded); 302 assert (obfWasAdded);
293 this.isDirty = true; 303 this.isDirty = true;
294 } 304 }
@@ -298,23 +308,23 @@ public class ClassMapping implements Comparable<ClassMapping> {
298 return methodsByObf.values(); 308 return methodsByObf.values();
299 } 309 }
300 310
301 public boolean containsObfMethod(String obfName, Signature obfSignature) { 311 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
302 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); 312 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
303 } 313 }
304 314
305 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { 315 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
306 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); 316 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
307 } 317 }
308 318
309 public void addMethodMapping(MethodMapping methodMapping) { 319 public void addMethodMapping(MethodMapping methodMapping) {
310 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 320 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
311 if (methodsByObf.containsKey(obfKey)) { 321 if (methodsByObf.containsKey(obfKey)) {
312 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 322 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
313 } 323 }
314 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; 324 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
315 assert (wasAdded); 325 assert (wasAdded);
316 if (methodMapping.getDeobfName() != null) { 326 if (!methodMapping.isObfuscated()) {
317 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); 327 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
318 if (methodsByDeobf.containsKey(deobfKey)) { 328 if (methodsByDeobf.containsKey(deobfKey)) {
319 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 329 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
320 } 330 }
@@ -326,44 +336,48 @@ public class ClassMapping implements Comparable<ClassMapping> {
326 } 336 }
327 337
328 public void removeMethodMapping(MethodMapping methodMapping) { 338 public void removeMethodMapping(MethodMapping methodMapping) {
329 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; 339 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
330 assert (obfWasRemoved); 340 assert (obfWasRemoved);
331 if (methodMapping.getDeobfName() != null) { 341 if (!methodMapping.isObfuscated()) {
332 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 342 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
333 assert (deobfWasRemoved); 343 assert (deobfWasRemoved);
334 } 344 }
335 this.isDirty = true; 345 this.isDirty = true;
336 } 346 }
337 347
338 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 348 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
339 return methodsByObf.get(getMethodKey(obfName, obfSignature)); 349 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
350 }
351
352 public MethodMapping getMethodByObf(MethodEntry method) {
353 return getMethodByObf(method.getName(), method.getDesc());
340 } 354 }
341 355
342 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { 356 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
343 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); 357 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
344 } 358 }
345 359
346 private String getMethodKey(String name, Signature signature) { 360 private String getMethodKey(String name, MethodDescriptor descriptor) {
347 if (name == null) { 361 if (name == null) {
348 throw new IllegalArgumentException("name cannot be null!"); 362 throw new IllegalArgumentException("name cannot be null!");
349 } 363 }
350 if (signature == null) { 364 if (descriptor == null) {
351 throw new IllegalArgumentException("signature cannot be null!"); 365 throw new IllegalArgumentException("descriptor cannot be null!");
352 } 366 }
353 return name + signature; 367 return name + descriptor;
354 } 368 }
355 369
356 public void setMethodName(String obfName, Signature obfSignature, String deobfName) { 370 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
357 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); 371 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
358 if (methodMapping == null) { 372 if (methodMapping == null) {
359 methodMapping = createMethodMapping(obfName, obfSignature); 373 methodMapping = createMethodMapping(obfName, obfDescriptor);
360 } else if (methodMapping.getDeobfName() != null) { 374 } else if (!methodMapping.isObfuscated()) {
361 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 375 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
362 assert (wasRemoved); 376 assert (wasRemoved);
363 } 377 }
364 methodMapping.setDeobfName(deobfName); 378 methodMapping.setDeobfName(deobfName);
365 if (deobfName != null) { 379 if (deobfName != null) {
366 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 380 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
367 assert (wasAdded); 381 assert (wasAdded);
368 } 382 }
369 this.isDirty = true; 383 this.isDirty = true;
@@ -371,35 +385,35 @@ public class ClassMapping implements Comparable<ClassMapping> {
371 385
372 //// ARGUMENTS //////// 386 //// ARGUMENTS ////////
373 387
374 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 388 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
375 assert (newObfName != null); 389 assert (newObfName != null);
376 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); 390 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
377 assert (methodMapping != null); 391 assert (methodMapping != null);
378 methodMapping.setObfName(newObfName); 392 methodMapping.setObfName(newObfName);
379 methodMapping.setObfSignature(newObfSignature); 393 methodMapping.setObfDescriptor(newObfDescriptor);
380 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 394 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
381 assert (obfWasAdded); 395 assert (obfWasAdded);
382 this.isDirty = true; 396 this.isDirty = true;
383 } 397 }
384 398
385 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 399 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
386 assert (argumentName != null); 400 assert (argumentName != null);
387 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); 401 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
388 if (methodMapping == null) { 402 if (methodMapping == null) {
389 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 403 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
390 } 404 }
391 methodMapping.setArgumentName(argumentIndex, argumentName); 405 methodMapping.setLocalVariableName(argumentIndex, argumentName);
392 this.isDirty = true; 406 this.isDirty = true;
393 } 407 }
394 408
395 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 409 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
396 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 410 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
397 this.isDirty = true; 411 this.isDirty = true;
398 } 412 }
399 413
400 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 414 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
401 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 415 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
402 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 416 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
403 assert (wasAdded); 417 assert (wasAdded);
404 this.isDirty = true; 418 this.isDirty = true;
405 return methodMapping; 419 return methodMapping;
@@ -459,24 +473,24 @@ public class ClassMapping implements Comparable<ClassMapping> {
459 473
460 // rename field types 474 // rename field types
461 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { 475 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
462 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 476 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
463 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { 477 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
464 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; 478 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
465 assert (wasRemoved); 479 assert (wasRemoved);
466 boolean wasAdded = fieldsByObf 480 boolean wasAdded = fieldsByObf
467 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; 481 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
468 assert (wasAdded); 482 assert (wasAdded);
469 } 483 }
470 } 484 }
471 485
472 // rename method signatures 486 // rename method signatures
473 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { 487 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
474 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 488 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
475 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { 489 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
476 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; 490 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
477 assert (wasRemoved); 491 assert (wasRemoved);
478 boolean wasAdded = methodsByObf 492 boolean wasAdded = methodsByObf
479 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; 493 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
480 assert (wasAdded); 494 assert (wasAdded);
481 } 495 }
482 } 496 }
@@ -490,9 +504,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
490 return false; 504 return false;
491 } 505 }
492 506
493 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 507 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
494 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); 508 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
495 return methodMapping != null && methodMapping.containsArgument(name); 509 return methodMapping != null && methodMapping.containsLocalVariable(name);
496 } 510 }
497 511
498 public ClassEntry getObfEntry() { 512 public ClassEntry getObfEntry() {
@@ -503,6 +517,14 @@ public class ClassMapping implements Comparable<ClassMapping> {
503 return deobfFullName != null ? new ClassEntry(deobfFullName) : null; 517 return deobfFullName != null ? new ClassEntry(deobfFullName) : null;
504 } 518 }
505 519
520 public boolean isObfuscated() {
521 return this.deobfName == null || this.deobfName.equals(this.obfFullName);
522 }
523
524 public String getSaveName() {
525 return this.isObfuscated() ? this.obfFullName : this.deobfName;
526 }
527
506 public boolean isDirty() { 528 public boolean isDirty() {
507 return isDirty; 529 return isDirty;
508 } 530 }
@@ -511,8 +533,8 @@ public class ClassMapping implements Comparable<ClassMapping> {
511 this.isDirty = false; 533 this.isDirty = false;
512 } 534 }
513 535
514 public void markDirty(){ 536 public void markDirty() {
515 isDirty = true; 537 this.isDirty = true;
516 } 538 }
517 539
518 public Mappings.EntryModifier getModifier() { 540 public Mappings.EntryModifier getModifier() {
@@ -525,9 +547,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
525 this.modifier = modifier; 547 this.modifier = modifier;
526 } 548 }
527 549
528 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { 550 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
529 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), 551 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
530 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); 552 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
531 553
532 if (fieldMapping.getModifier() != modifier) { 554 if (fieldMapping.getModifier() != modifier) {
533 fieldMapping.setModifier(modifier); 555 fieldMapping.setModifier(modifier);
@@ -535,7 +557,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
535 } 557 }
536 } 558 }
537 559
538 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { 560 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
539 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), 561 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
540 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); 562 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
541 563
@@ -550,4 +572,30 @@ public class ClassMapping implements Comparable<ClassMapping> {
550 this.deobfFullName = deobName; 572 this.deobfFullName = deobName;
551 return this; 573 return this;
552 } 574 }
575
576 public ClassMapping copy() {
577 ClassMapping copied = new ClassMapping(this.obfFullName);
578 copied.obfSimpleName= this.obfSimpleName;
579 copied.modifier = this.modifier;
580 copied.deobfFullName = this.deobfFullName;
581 copied.deobfName = this.deobfName;
582 copied.innerClassesByDeobf = this.innerClassesByDeobf;
583 copied.innerClassesByObfFull = this.innerClassesByObfFull;
584 copied.innerClassesByObfSimple = this.innerClassesByObfSimple;
585 copied.fieldsByObf = this.fieldsByObf;
586 copied.fieldsByDeobf = this.fieldsByDeobf;
587 copied.methodsByObf = this.methodsByObf;
588 copied.methodsByDeobf = this.methodsByDeobf;
589 return copied;
590 }
591
592 @Override
593 public int hashCode() {
594 return this.obfFullName.hashCode();
595 }
596
597 @Override
598 public boolean equals(Object obj) {
599 return obj instanceof ClassMapping && ((ClassMapping) obj).obfFullName.equals(this.obfFullName);
600 }
553} 601}
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..b0bb129d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
@@ -0,0 +1,370 @@
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;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Map;
23
24public class DirectionalTranslator implements Translator {
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;
52 if (entry.isArray()) {
53 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
54 } else {
55 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
56 }
57 return new ClassEntry(className);
58 }
59
60 @Override
61 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
62 String className;
63 if (entry.isArray()) {
64 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
65 } else {
66 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
67 }
68 Signature translatedSignature = this.getTranslatedSignature(entry.getSignature());
69 return new ClassDefEntry(className, translatedSignature, getClassModifier(entry).transform(entry.getAccess()));
70 }
71
72 private String translateClassName(ClassEntry entry) {
73 // normal classes are easy
74 ClassMapping classMapping = this.classes.get(entry.getName());
75 if (classMapping == null) {
76 return entry.getName();
77 }
78 return classMapping.getTranslatedName(direction);
79 }
80
81 private String translateInnerClassName(ClassEntry entry) {
82 // translate as much of the class chain as we can
83 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
84 String[] obfClassNames = entry.getName().split("\\$");
85 StringBuilder buf = new StringBuilder();
86 for (int i = 0; i < obfClassNames.length; i++) {
87 boolean isFirstClass = buf.length() == 0;
88 String className = null;
89 ClassMapping classMapping = mappingsChain.get(i);
90 if (classMapping != null) {
91 className = this.direction.choose(
92 classMapping.getDeobfName(),
93 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
94 );
95 }
96 if (className == null) {
97 className = obfClassNames[i];
98 }
99 if (!isFirstClass) {
100 buf.append("$");
101 }
102 buf.append(className);
103 }
104 return buf.toString();
105 }
106
107 @Override
108 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
109 String translatedName = translateFieldName(entry);
110 if (translatedName == null) {
111 translatedName = entry.getName();
112 }
113 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
114 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
115 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
116 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
117 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, translatedAccess);
118 }
119
120 @Override
121 public FieldEntry getTranslatedField(FieldEntry entry) {
122 String translatedName = translateFieldName(entry);
123 if (translatedName == null) {
124 translatedName = entry.getName();
125 }
126 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
127 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
128 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
129 }
130
131 private String translateFieldName(FieldEntry entry) {
132 // resolve the class entry
133 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry, true);
134 if (resolvedClassEntry != null) {
135 // look for the class
136 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
137 if (classMapping != null) {
138 // look for the field
139 FieldMapping mapping = this.direction.choose(
140 classMapping.getFieldByObf(entry.getName(), entry.getDesc()),
141 classMapping.getFieldByDeobf(entry.getName(), getTranslatedTypeDesc(entry.getDesc()))
142 );
143 if (mapping != null) {
144 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
145 }
146 }
147 }
148 return null;
149 }
150
151 @Override
152 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
153 String translatedName = translateMethodName(entry);
154 if (translatedName == null) {
155 translatedName = entry.getName();
156 }
157 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
158 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
159 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
160 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
161 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, access);
162 }
163
164 @Override
165 public MethodEntry getTranslatedMethod(MethodEntry entry) {
166 String translatedName = translateMethodName(entry);
167 if (translatedName == null) {
168 translatedName = entry.getName();
169 }
170 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
171 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
172 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
173 }
174
175 private String translateMethodName(MethodEntry entry) {
176 // resolve the class entry
177 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry, true);
178 if (resolvedOwner != null) {
179 // look for class
180 ClassMapping classMapping = findClassMapping(resolvedOwner);
181 if (classMapping != null) {
182 // look for the method
183 MethodMapping mapping = this.direction.choose(
184 classMapping.getMethodByObf(entry.getName(), entry.getDesc()),
185 classMapping.getMethodByDeobf(entry.getName(), getTranslatedMethodDesc(entry.getDesc()))
186 );
187 if (mapping != null) {
188 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
189 }
190 }
191 }
192 return null;
193 }
194
195 @Override
196 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
197 String translatedArgumentName = translateLocalVariableName(entry);
198 if (translatedArgumentName == null) {
199 translatedArgumentName = inheritLocalVariableName(entry);
200 }
201 if (translatedArgumentName == null) {
202 translatedArgumentName = entry.getName();
203 }
204 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
205 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
206 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName);
207 }
208
209 @Override
210 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
211 String translatedArgumentName = translateLocalVariableName(entry);
212 if (translatedArgumentName == null) {
213 translatedArgumentName = inheritLocalVariableName(entry);
214 }
215 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
216 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
217 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
218 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName != null ? translatedArgumentName : entry.getName(), translatedTypeDesc);
219 }
220
221 @Override
222 public boolean hasClassMapping(ClassEntry entry) {
223 return classes.containsKey(entry.getName());
224 }
225
226 @Override
227 public boolean hasFieldMapping(FieldEntry entry) {
228 return translateFieldName(entry) != null;
229 }
230
231 @Override
232 public boolean hasMethodMapping(MethodEntry entry) {
233 return translateMethodName(entry) != null;
234 }
235
236 @Override
237 public boolean hasLocalVariableMapping(LocalVariableEntry entry) {
238 return translateLocalVariableName(entry) != null || inheritLocalVariableName(entry) != null;
239 }
240
241 // TODO: support not identical behavior (specific to constructor)
242 private String translateLocalVariableName(LocalVariableEntry entry) {
243 // look for identical behavior in superclasses
244 ClassEntry ownerEntry = entry.getOwnerClassEntry();
245 if (ownerEntry != null) {
246 // look for the class
247 ClassMapping classMapping = findClassMapping(ownerEntry);
248 if (classMapping != null) {
249 // look for the method
250 MethodMapping methodMapping = this.direction.choose(
251 classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()),
252 classMapping.getMethodByDeobf(entry.getMethodName(), getTranslatedMethodDesc(entry.getMethodDesc()))
253 );
254 if (methodMapping != null) {
255 int index = entry.getIndex();
256 return this.direction.choose(
257 methodMapping.getDeobfLocalVariableName(index),
258 methodMapping.getObfLocalVariableName(index)
259 );
260 }
261 }
262 }
263 return null;
264 }
265
266 private String inheritLocalVariableName(LocalVariableEntry entry) {
267 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
268 // Check in mother class for the arg
269 for (ClassEntry ancestorEntry : ancestry) {
270 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
271 if (this.index.entryExists(motherArg)) {
272 String result = translateLocalVariableName(motherArg);
273 if (result != null) {
274 return result;
275 }
276 }
277 }
278 return null;
279 }
280
281 @Override
282 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
283 return desc.remap(this::remapClass);
284 }
285
286 @Override
287 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
288 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
289 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
290 for (TypeDescriptor argument : arguments) {
291 translatedArguments.add(getTranslatedTypeDesc(argument));
292 }
293 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
294 }
295
296 @Override
297 public Signature getTranslatedSignature(Signature signature) {
298 if (signature == null) {
299 return null;
300 }
301 return signature.remap(this::remapClass);
302 }
303
304 private ClassMapping findClassMapping(ClassEntry entry) {
305 List<ClassMapping> mappingChain = getClassMappingChain(entry);
306 return mappingChain.get(mappingChain.size() - 1);
307 }
308
309 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
310
311 // get a list of all the classes in the hierarchy
312 String[] parts = entry.getName().split("\\$");
313 List<ClassMapping> mappingsChain = Lists.newArrayList();
314
315 // get mappings for the outer class
316 ClassMapping outerClassMapping = this.classes.get(parts[0]);
317 mappingsChain.add(outerClassMapping);
318
319 for (int i = 1; i < parts.length; i++) {
320
321 // get mappings for the inner class
322 ClassMapping innerClassMapping = null;
323 if (outerClassMapping != null) {
324 innerClassMapping = this.direction.choose(
325 outerClassMapping.getInnerClassByObfSimple(parts[i]),
326 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
327 );
328 }
329 mappingsChain.add(innerClassMapping);
330 outerClassMapping = innerClassMapping;
331 }
332
333 assert (mappingsChain.size() == parts.length);
334 return mappingsChain;
335 }
336
337 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
338 ClassMapping classMapping = findClassMapping(entry);
339 if (classMapping != null) {
340 return classMapping.getModifier();
341 }
342 return Mappings.EntryModifier.UNCHANGED;
343 }
344
345 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
346 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
347 if (classMapping != null) {
348 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
349 if (fieldMapping != null) {
350 return fieldMapping.getModifier();
351 }
352 }
353 return Mappings.EntryModifier.UNCHANGED;
354 }
355
356 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
357 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
358 if (classMapping != null) {
359 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
360 if (methodMapping != null) {
361 return methodMapping.getModifier();
362 }
363 }
364 return Mappings.EntryModifier.UNCHANGED;
365 }
366
367 private String remapClass(String name) {
368 return getTranslatedClass(new ClassEntry(name)).getName();
369 }
370}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
deleted file mode 100644
index 993bb64b..00000000
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ /dev/null
@@ -1,132 +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.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
22public 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) {
29 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
30 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
31 }
32
33 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
34 return new ClassEntry(classMapping.getObfFullName());
35 }
36
37 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
38 return new ClassEntry(classMapping.getDeobfName());
39 }
40
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) {
58 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType());
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 }
116
117 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) {
118 if (behaviorName.equals("<clinit>")) {
119 return new ConstructorEntry(classEntry);
120 } else {
121 throw new IllegalArgumentException("Only class initializers don't have signatures");
122 }
123 }
124
125 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) {
126 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature());
127 }
128
129 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) {
130 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping);
131 }
132}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
deleted file mode 100644
index 0f1f5065..00000000
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.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.mapping;
13
14import cuchaz.enigma.utils.Utils;
15
16public class FieldEntry implements Entry {
17
18 private ClassEntry classEntry;
19 private String name;
20 private Type type;
21
22 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public FieldEntry(ClassEntry classEntry, String name, Type type) {
24 if (classEntry == null) {
25 throw new IllegalArgumentException("Class cannot be null!");
26 }
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
34 this.classEntry = classEntry;
35 this.name = name;
36 this.type = type;
37 }
38
39 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
40 this.classEntry = newClassEntry;
41 this.name = other.name;
42 this.type = other.type;
43 }
44
45 @Override
46 public ClassEntry getClassEntry() {
47 return this.classEntry;
48 }
49
50 @Override
51 public String getName() {
52 return this.name;
53 }
54
55 @Override
56 public String getClassName() {
57 return this.classEntry.getName();
58 }
59
60 public Type getType() {
61 return this.type;
62 }
63
64 @Override
65 public FieldEntry cloneToNewClass(ClassEntry classEntry) {
66 return new FieldEntry(this, classEntry);
67 }
68
69 @Override
70 public int hashCode() {
71 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type);
72 }
73
74 @Override
75 public boolean equals(Object other) {
76 return other instanceof FieldEntry && equals((FieldEntry) other);
77 }
78
79 public boolean equals(FieldEntry other) {
80 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type);
81 }
82
83 @Override
84 public String toString() {
85 return this.classEntry.getName() + "." + this.name + ":" + this.type;
86 }
87}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index cd761b47..8fbe095b 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -11,32 +11,27 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.FieldEntry;
14import cuchaz.enigma.throwables.IllegalNameException; 16import cuchaz.enigma.throwables.IllegalNameException;
15 17
16public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { 18public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
17 19
18 private String obfName; 20 private String obfName;
19 private String deobfName; 21 private String deobfName;
20 private Type obfType; 22 private TypeDescriptor obfDesc;
21 private Mappings.EntryModifier modifier; 23 private Mappings.EntryModifier modifier;
22 24
23 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { 25 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
24 this.obfName = obfName; 26 this.obfName = obfName;
25 this.deobfName = NameValidator.validateFieldName(deobfName); 27 this.deobfName = NameValidator.validateFieldName(deobfName);
26 this.obfType = obfType; 28 this.obfDesc = obfDesc;
27 this.modifier = modifier; 29 this.modifier = modifier;
28 } 30 }
29 31
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 32 @Override
38 public FieldEntry getObfEntry(ClassEntry classEntry) { 33 public FieldEntry getObfEntry(ClassEntry classEntry) {
39 return new FieldEntry(classEntry, this.obfName, this.obfType); 34 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
40 } 35 }
41 36
42 @Override 37 @Override
@@ -65,12 +60,12 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
65 this.deobfName = NameValidator.validateFieldName(val); 60 this.deobfName = NameValidator.validateFieldName(val);
66 } 61 }
67 62
68 public Type getObfType() { 63 public TypeDescriptor getObfDesc() {
69 return this.obfType; 64 return this.obfDesc;
70 } 65 }
71 66
72 public void setObfType(Type val) { 67 public void setObfDesc(TypeDescriptor val) {
73 this.obfType = val; 68 this.obfDesc = val;
74 } 69 }
75 70
76 public Mappings.EntryModifier getModifier() { 71 public Mappings.EntryModifier getModifier() {
@@ -83,21 +78,20 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
83 78
84 @Override 79 @Override
85 public int compareTo(FieldMapping other) { 80 public int compareTo(FieldMapping other) {
86 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 81 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
87 } 82 }
88 83
89 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 84 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
90 // rename obf classes in the type 85 // rename obf classes in the desc
91 Type newType = new Type(this.obfType, className -> 86 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
92 {
93 if (className.equals(oldObfClassName)) { 87 if (className.equals(oldObfClassName)) {
94 return newObfClassName; 88 return newObfClassName;
95 } 89 }
96 return null; 90 return className;
97 }); 91 });
98 92
99 if (!newType.equals(this.obfType)) { 93 if (!newDesc.equals(this.obfDesc)) {
100 this.obfType = newType; 94 this.obfDesc = newDesc;
101 return true; 95 return true;
102 } 96 }
103 return false; 97 return false;
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
deleted file mode 100644
index 2bb5e3f7..00000000
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
+++ /dev/null
@@ -1,102 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import cuchaz.enigma.utils.Utils;
4
5/**
6 * Desc...
7 * Created by Thog
8 * 19/10/2016
9 */
10public class LocalVariableEntry implements Entry {
11
12 protected final BehaviorEntry behaviorEntry;
13 protected final String name;
14 protected final Type type;
15 protected final int index;
16
17 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) {
18 if (behaviorEntry == null) {
19 throw new IllegalArgumentException("Behavior cannot be null!");
20 }
21 if (index < 0) {
22 throw new IllegalArgumentException("Index must be non-negative!");
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;
33 this.type = type;
34 this.index = index;
35 }
36
37 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) {
38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry);
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 }
51
52 public int getIndex() {
53 return index;
54 }
55
56 @Override
57 public String getName() {
58 return this.name;
59 }
60
61 @Override
62 public ClassEntry getClassEntry() {
63 return this.behaviorEntry.getClassEntry();
64 }
65
66 @Override
67 public String getClassName() {
68 return this.behaviorEntry.getClassName();
69 }
70
71 @Override
72 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) {
73 return new LocalVariableEntry(this, classEntry);
74 }
75
76 public String getMethodName() {
77 return this.behaviorEntry.getName();
78 }
79
80 public Signature getMethodSignature() {
81 return this.behaviorEntry.getSignature();
82 }
83
84 @Override
85 public int hashCode() {
86 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
87 }
88
89 @Override
90 public boolean equals(Object other) {
91 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
92 }
93
94 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;
96 }
97
98 @Override
99 public String toString() {
100 return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")";
101 }
102}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
index 91ecd106..62dbcf31 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
@@ -11,18 +11,21 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public class ArgumentMapping implements Comparable<ArgumentMapping> { 14import cuchaz.enigma.mapping.entry.LocalVariableEntry;
15import cuchaz.enigma.mapping.entry.MethodEntry;
16
17public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
15 18
16 private int index; 19 private int index;
17 private String name; 20 private String name;
18 21
19 // NOTE: this argument order is important for the MethodReader/MethodWriter 22 // NOTE: this argument order is important for the MethodReader/MethodWriter
20 public ArgumentMapping(int index, String name) { 23 public LocalVariableMapping(int index, String name) {
21 this.index = index; 24 this.index = index;
22 this.name = NameValidator.validateArgumentName(name); 25 this.name = NameValidator.validateArgumentName(name);
23 } 26 }
24 27
25 public ArgumentMapping(ArgumentMapping other) { 28 public LocalVariableMapping(LocalVariableMapping other) {
26 this.index = other.index; 29 this.index = other.index;
27 this.name = other.name; 30 this.name = other.name;
28 } 31 }
@@ -39,12 +42,12 @@ public class ArgumentMapping implements Comparable<ArgumentMapping> {
39 this.name = NameValidator.validateArgumentName(val); 42 this.name = NameValidator.validateArgumentName(val);
40 } 43 }
41 44
42 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { 45 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
43 return new ArgumentEntry(behaviorEntry, index, name); 46 return new LocalVariableEntry(methodEntry, index, name);
44 } 47 }
45 48
46 @Override 49 @Override
47 public int compareTo(ArgumentMapping other) { 50 public int compareTo(LocalVariableMapping other) {
48 return Integer.compare(this.index, other.index); 51 return Integer.compare(this.index, other.index);
49 } 52 }
50} 53}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index cf78ca30..3ef1be52 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -15,11 +15,15 @@ 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;
19import cuchaz.enigma.mapping.entry.ClassEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.MappingConflict; 21import cuchaz.enigma.throwables.MappingConflict;
19 22
20import java.io.File; 23import java.io.File;
21import java.io.IOException; 24import java.io.IOException;
22import java.util.*; 25import java.util.*;
26import java.util.stream.Collectors;
23 27
24public class Mappings { 28public class Mappings {
25 29
@@ -96,11 +100,11 @@ public class Mappings {
96 100
97 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 101 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
98 switch (direction) { 102 switch (direction) {
99 case Deobfuscating: 103 case DEOBFUSCATING:
100 104
101 return new Translator(direction, this.classesByObf, index); 105 return new DirectionalTranslator(direction, this.classesByObf, index);
102 106
103 case Obfuscating: 107 case OBFUSCATING:
104 108
105 // fill in the missing deobf class entries with obf entries 109 // fill in the missing deobf class entries with obf entries
106 Map<String, ClassMapping> classes = Maps.newHashMap(); 110 Map<String, ClassMapping> classes = Maps.newHashMap();
@@ -114,9 +118,9 @@ public class Mappings {
114 118
115 // translate the translation index 119 // translate the translation index
116 // NOTE: this isn't actually recursive 120 // NOTE: this isn't actually recursive
117 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 121 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
118 122
119 return new Translator(direction, classes, deobfIndex); 123 return new DirectionalTranslator(direction, classes, deobfIndex);
120 124
121 default: 125 default:
122 throw new Error("Invalid translation direction!"); 126 throw new Error("Invalid translation direction!");
@@ -151,9 +155,9 @@ public class Mappings {
151 155
152 // add classes from method signatures 156 // add classes from method signatures
153 for (MethodMapping methodMapping : classMapping.methods()) { 157 for (MethodMapping methodMapping : classMapping.methods()) {
154 for (Type type : methodMapping.getObfSignature().types()) { 158 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
155 if (type.hasClass()) { 159 if (desc.containsType()) {
156 classNames.add(type.getClassEntry().getClassName()); 160 classNames.add(desc.getTypeEntry().getClassName());
157 } 161 }
158 } 162 }
159 } 163 }
@@ -165,9 +169,9 @@ public class Mappings {
165 return this.classesByDeobf.containsKey(deobfName); 169 return this.classesByDeobf.containsKey(deobfName);
166 } 170 }
167 171
168 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 172 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
169 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 173 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
170 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 174 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
171 } 175 }
172 176
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 177 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
@@ -180,14 +184,14 @@ public class Mappings {
180 return false; 184 return false;
181 } 185 }
182 186
183 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 187 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
184 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 188 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
185 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 189 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
186 } 190 }
187 191
188 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 192 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
189 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 193 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
190 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 194 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
191 } 195 }
192 196
193 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 197 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
@@ -210,8 +214,14 @@ public class Mappings {
210 214
211 public void savePreviousState() { 215 public void savePreviousState() {
212 this.previousState = new Mappings(this.originMapping); 216 this.previousState = new Mappings(this.originMapping);
213 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); 217 this.previousState.classesByDeobf = new HashMap<>();
214 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); 218 for (Map.Entry<String, ClassMapping> entry : this.classesByDeobf.entrySet()) {
219 this.previousState.classesByDeobf.put(entry.getKey(), entry.getValue().copy());
220 }
221 this.previousState.classesByObf = new HashMap<>();
222 for (Map.Entry<String, ClassMapping> entry : this.classesByObf.entrySet()) {
223 this.previousState.classesByObf.put(entry.getKey(), entry.getValue().copy());
224 }
215 classesByDeobf.values().forEach(ClassMapping::resetDirty); 225 classesByDeobf.values().forEach(ClassMapping::resetDirty);
216 classesByObf.values().forEach(ClassMapping::resetDirty); 226 classesByObf.values().forEach(ClassMapping::resetDirty);
217 } 227 }
@@ -239,5 +249,19 @@ public class Mappings {
239 public String getFormattedName() { 249 public String getFormattedName() {
240 return " ACC:" + super.toString(); 250 return " ACC:" + super.toString();
241 } 251 }
252
253 public AccessFlags transform(AccessFlags access) {
254 switch (this) {
255 case PUBLIC:
256 return access.setPublic();
257 case PROTECTED:
258 return access.setProtected();
259 case PRIVATE:
260 return access.setPrivate();
261 case UNCHANGED:
262 default:
263 return access;
264 }
265 }
242 } 266 }
243} 267}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index 172641bd..a42f255a 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -14,6 +14,10 @@ package cuchaz.enigma.mapping;
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.JarIndex; 16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry;
18import cuchaz.enigma.mapping.entry.EntryFactory;
19import cuchaz.enigma.mapping.entry.FieldEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
17 21
18import java.util.Map; 22import java.util.Map;
19 23
@@ -23,7 +27,7 @@ public class MappingsChecker {
23 private Map<ClassEntry, ClassMapping> droppedClassMappings; 27 private Map<ClassEntry, ClassMapping> droppedClassMappings;
24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; 28 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
25 private Map<FieldEntry, FieldMapping> droppedFieldMappings; 29 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
26 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; 30 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
27 31
28 public MappingsChecker(JarIndex index) { 32 public MappingsChecker(JarIndex index) {
29 this.index = index; 33 this.index = index;
@@ -45,7 +49,7 @@ public class MappingsChecker {
45 return this.droppedFieldMappings; 49 return this.droppedFieldMappings;
46 } 50 }
47 51
48 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 52 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
49 return this.droppedMethodMappings; 53 return this.droppedMethodMappings;
50 } 54 }
51 55
@@ -77,10 +81,10 @@ public class MappingsChecker {
77 81
78 // check methods 82 // check methods
79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 83 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
80 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 84 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
81 if (!this.index.containsObfBehavior(obfBehaviorEntry)) { 85 if (!this.index.containsObfMethod(obfMethodEntry)) {
82 classMapping.removeMethodMapping(methodMapping); 86 classMapping.removeMethodMapping(methodMapping);
83 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 87 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
84 } 88 }
85 } 89 }
86 90
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index a0d43133..ddbee76f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -5,8 +5,14 @@ import com.google.common.collect.Queues;
5import cuchaz.enigma.throwables.MappingConflict; 5import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException; 6import cuchaz.enigma.throwables.MappingParseException;
7 7
8import java.io.*; 8import java.io.BufferedReader;
9import java.io.File;
10import java.io.FileInputStream;
11import java.io.IOException;
12import java.io.InputStream;
13import java.io.InputStreamReader;
9import java.util.Deque; 14import java.util.Deque;
15import java.util.function.Supplier;
10 16
11public class MappingsEnigmaReader { 17public class MappingsEnigmaReader {
12 18
@@ -39,92 +45,95 @@ public class MappingsEnigmaReader {
39 } 45 }
40 46
41 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { 47 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException {
48 return readFileStream(mappings, new FileInputStream(file), file::getAbsolutePath);
49 }
42 50
43 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); 51 public Mappings readFileStream(Mappings mappings, InputStream stream, Supplier<String> filenameSupplier) throws IOException, MappingParseException {
44 Deque<Object> mappingStack = Queues.newArrayDeque(); 52 try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, Charsets.UTF_8))) {
53 Deque<Object> mappingStack = Queues.newArrayDeque();
45 54
46 int lineNumber = 0; 55 int lineNumber = 0;
47 String line; 56 String line;
48 while ((line = in.readLine()) != null) { 57 while ((line = in.readLine()) != null) {
49 lineNumber++; 58 lineNumber++;
50 59
51 // strip comments 60 // strip comments
52 int commentPos = line.indexOf('#'); 61 int commentPos = line.indexOf('#');
53 if (commentPos >= 0) { 62 if (commentPos >= 0) {
54 line = line.substring(0, commentPos); 63 line = line.substring(0, commentPos);
55 } 64 }
56 65
57 // skip blank lines 66 // skip blank lines
58 if (line.trim().length() <= 0) { 67 if (line.trim().length() <= 0) {
59 continue; 68 continue;
60 } 69 }
61 70
62 // get the indent of this line 71 // get the indent of this line
63 int indent = 0; 72 int indent = 0;
64 for (int i = 0; i < line.length(); i++) { 73 for (int i = 0; i < line.length(); i++) {
65 if (line.charAt(i) != '\t') { 74 if (line.charAt(i) != '\t') {
66 break; 75 break;
76 }
77 indent++;
67 } 78 }
68 indent++;
69 }
70 79
71 // handle stack pops 80 // handle stack pops
72 while (indent < mappingStack.size()) { 81 while (indent < mappingStack.size()) {
73 mappingStack.pop(); 82 mappingStack.pop();
74 } 83 }
75 84
76 String[] parts = line.trim().split("\\s"); 85 String[] parts = line.trim().split("\\s");
77 try { 86 try {
78 // read the first token 87 // read the first token
79 String token = parts[0]; 88 String token = parts[0];
80 89
81 if (token.equalsIgnoreCase("CLASS")) { 90 if (token.equalsIgnoreCase("CLASS")) {
82 ClassMapping classMapping; 91 ClassMapping classMapping;
83 if (indent <= 0) { 92 if (indent <= 0) {
84 // outer class 93 // outer class
85 classMapping = readClass(parts, false); 94 classMapping = readClass(parts, false);
86 mappings.addClassMapping(classMapping); 95 mappings.addClassMapping(classMapping);
87 } else { 96 } else {
88 97
89 // inner class 98 // inner class
90 if (!(mappingStack.peek() instanceof ClassMapping)) { 99 if (!(mappingStack.peek() instanceof ClassMapping)) {
91 throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); 100 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected CLASS entry here!");
101 }
102
103 classMapping = readClass(parts, true);
104 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping);
92 } 105 }
93 106 mappingStack.push(classMapping);
94 classMapping = readClass(parts, true); 107 } else if (token.equalsIgnoreCase("FIELD")) {
95 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); 108 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
96 } 109 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected FIELD entry here!");
97 mappingStack.push(classMapping); 110 }
98 } else if (token.equalsIgnoreCase("FIELD")) { 111 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts));
99 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 112 } else if (token.equalsIgnoreCase("METHOD")) {
100 throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); 113 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
101 } 114 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected METHOD entry here!");
102 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); 115 }
103 } else if (token.equalsIgnoreCase("METHOD")) { 116 MethodMapping methodMapping = readMethod(parts);
104 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 117 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping);
105 throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); 118 mappingStack.push(methodMapping);
106 } 119 } else if (token.equalsIgnoreCase("ARG")) {
107 MethodMapping methodMapping = readMethod(parts); 120 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
108 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); 121 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected ARG entry here!");
109 mappingStack.push(methodMapping); 122 }
110 } else if (token.equalsIgnoreCase("ARG")) { 123 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts));
111 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
112 throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!");
113 } 124 }
114 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); 125 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
126 throw new MappingParseException(filenameSupplier, lineNumber, "Malformed line:\n" + line);
127 } catch (MappingConflict e) {
128 throw new MappingParseException(filenameSupplier, lineNumber, e.getMessage());
115 } 129 }
116 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
117 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
118 } catch (MappingConflict e) {
119 throw new MappingParseException(file, lineNumber, e.getMessage());
120 } 130 }
131 return mappings;
121 } 132 }
122 in.close();
123 return mappings;
124 } 133 }
125 134
126 private ArgumentMapping readArgument(String[] parts) { 135 private LocalVariableMapping readArgument(String[] parts) {
127 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); 136 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
128 } 137 }
129 138
130 private ClassMapping readClass(String[] parts, boolean makeSimple) { 139 private ClassMapping readClass(String[] parts, boolean makeSimple) {
@@ -150,27 +159,27 @@ public class MappingsEnigmaReader {
150 if (parts.length == 4) { 159 if (parts.length == 4) {
151 boolean access = parts[3].startsWith("ACC:"); 160 boolean access = parts[3].startsWith("ACC:");
152 if (access) 161 if (access)
153 mapping = new FieldMapping(parts[1], new Type(parts[2]), null, 162 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
154 Mappings.EntryModifier.valueOf(parts[3].substring(4))); 163 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
155 else 164 else
156 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); 165 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
157 } else if (parts.length == 5) 166 } else if (parts.length == 5)
158 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); 167 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
159 return mapping; 168 return mapping;
160 } 169 }
161 170
162 private MethodMapping readMethod(String[] parts) { 171 private MethodMapping readMethod(String[] parts) {
163 MethodMapping mapping = null; 172 MethodMapping mapping = null;
164 if (parts.length == 3) 173 if (parts.length == 3)
165 mapping = new MethodMapping(parts[1], new Signature(parts[2])); 174 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
166 else if (parts.length == 4) { 175 else if (parts.length == 4) {
167 boolean access = parts[3].startsWith("ACC:"); 176 boolean access = parts[3].startsWith("ACC:");
168 if (access) 177 if (access)
169 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); 178 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
170 else 179 else
171 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); 180 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
172 } else if (parts.length == 5) 181 } else if (parts.length == 5)
173 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], 182 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
174 Mappings.EntryModifier.valueOf(parts[4].substring(4))); 183 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
175 return mapping; 184 return mapping;
176 } 185 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index ba1b258b..b29990f5 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -14,9 +14,7 @@ package cuchaz.enigma.mapping;
14import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
15 15
16import java.io.*; 16import java.io.*;
17import java.util.ArrayList; 17import java.util.*;
18import java.util.Collections;
19import java.util.List;
20 18
21public class MappingsEnigmaWriter { 19public class MappingsEnigmaWriter {
22 20
@@ -33,83 +31,67 @@ public class MappingsEnigmaWriter {
33 if (!target.exists() && !target.mkdirs()) 31 if (!target.exists() && !target.mkdirs())
34 throw new IOException("Cannot create mapping directory!"); 32 throw new IOException("Cannot create mapping directory!");
35 33
34 Mappings previousState = mappings.getPreviousState();
36 for (ClassMapping classMapping : sorted(mappings.classes())) { 35 for (ClassMapping classMapping : sorted(mappings.classes())) {
37 if (!classMapping.isDirty()) 36 if (!classMapping.isDirty()) {
38 continue; 37 continue;
39 this.deletePreviousClassMapping(target, classMapping);
40 File obFile = new File(target, classMapping.getObfFullName() + ".mapping");
41 File result;
42 if (classMapping.getDeobfName() == null)
43 result = obFile;
44 else {
45 // Make sure that old version of the file doesn't exist
46 if (obFile.exists())
47 obFile.delete();
48 result = new File(target, classMapping.getDeobfName() + ".mapping");
49 } 38 }
50 39
51 if (!result.getParentFile().exists()) 40 if (previousState != null) {
52 result.getParentFile().mkdirs(); 41 ClassMapping previousClass = previousState.classesByObf.get(classMapping.getObfFullName());
42 File previousFile;
43 if (previousClass != null) {
44 previousFile = new File(target, previousClass.getSaveName() + ".mapping");
45 } else {
46 previousFile = new File(target, classMapping.getObfFullName() + ".mapping");
47 }
48 if (previousFile.exists() && !previousFile.delete()) {
49 System.err.println("Failed to delete old class mapping " + previousFile.getName());
50 }
51 }
52
53 File result = new File(target, classMapping.getSaveName() + ".mapping");
54
55 File packageFile = result.getParentFile();
56 if (!packageFile.exists()) {
57 packageFile.mkdirs();
58 }
53 result.createNewFile(); 59 result.createNewFile();
54 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(result), Charsets.UTF_8)); 60
55 write(outputWriter, classMapping, 0); 61 try (PrintWriter outputWriter = new PrintWriter(new BufferedWriter(new FileWriter(result)))) {
56 outputWriter.close(); 62 write(outputWriter, classMapping, 0);
63 }
57 } 64 }
58 65
59 // Remove dropped mappings 66 // Remove dropped mappings
60 if (mappings.getPreviousState() != null) { 67 if (previousState != null) {
61 List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); 68 Set<ClassMapping> droppedClassMappings = new HashSet<>(previousState.classes());
62 List<ClassMapping> classMappings = new ArrayList<>(mappings.classes()); 69 droppedClassMappings.removeAll(mappings.classes());
63 droppedClassMappings.removeAll(classMappings); 70 for (ClassMapping droppedMapping : droppedClassMappings) {
64 for (ClassMapping classMapping : droppedClassMappings) { 71 File result = new File(target, droppedMapping.getSaveName() + ".mapping");
65 File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); 72 if (!result.exists()) {
66 File result; 73 continue;
67 if (classMapping.getDeobfName() == null) 74 }
68 result = obFile; 75 if (!result.delete()) {
69 else { 76 System.err.println("Failed to delete dropped class mapping " + result.getName());
70 // Make sure that old version of the file doesn't exist
71 if (obFile.exists())
72 obFile.delete();
73 result = new File(target, classMapping.getDeobfName() + ".mapping");
74 } 77 }
75 if (result.exists())
76 result.delete();
77 } 78 }
78 } 79 }
79 } 80 }
80 81
81 private void deletePreviousClassMapping(File target, ClassMapping classMapping) {
82 File prevFile = null;
83 // Deob rename
84 if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) {
85 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
86 }
87 // Deob to ob rename
88 else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) {
89 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
90 }
91 // Ob to Deob rename
92 else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) {
93 prevFile = new File(target, classMapping.getObfFullName() + ".mapping");
94 }
95
96 if (prevFile != null && prevFile.exists())
97 prevFile.delete();
98 }
99
100 public void write(PrintWriter out, Mappings mappings) throws IOException { 82 public void write(PrintWriter out, Mappings mappings) throws IOException {
101 for (ClassMapping classMapping : sorted(mappings.classes())) { 83 for (ClassMapping classMapping : sorted(mappings.classes())) {
102 write(out, classMapping, 0); 84 write(out, classMapping, 0);
103 } 85 }
104 } 86 }
105 87
106 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { 88 protected void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
107 if (classMapping.getDeobfName() == null) { 89 if (classMapping.getDeobfName() == null) {
108 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), 90 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(),
109 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 91 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
110 } else { 92 } else {
111 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), 93 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(),
112 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 94 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
113 } 95 }
114 96
115 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 97 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
@@ -127,32 +109,32 @@ public class MappingsEnigmaWriter {
127 109
128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { 110 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
129 if (fieldMapping.getDeobfName() == null) 111 if (fieldMapping.getDeobfName() == null)
130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), 112 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 113 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
132 else 114 else
133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), 115 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()); 116 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
135 } 117 }
136 118
137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 119 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
138 if (methodMapping.getDeobfName() == null) { 120 if (methodMapping.isObfuscated()) {
139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), 121 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 122 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
141 } else { 123 } else {
142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), 124 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()); 125 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
144 } 126 }
145 127
146 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 128 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
147 write(out, argumentMapping, depth + 1); 129 write(out, localVariableMapping, depth + 1);
148 } 130 }
149 } 131 }
150 132
151 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { 133 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
152 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); 134 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
153 } 135 }
154 136
155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 137 protected <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
156 List<T> out = new ArrayList<>(); 138 List<T> out = new ArrayList<>();
157 for (T t : classes) { 139 for (T t : classes) {
158 out.add(t); 140 out.add(t);
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 7126d2b6..85b6d2ab 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -13,6 +13,7 @@ package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex; 15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.mapping.entry.*;
16import cuchaz.enigma.throwables.IllegalNameException; 17import cuchaz.enigma.throwables.IllegalNameException;
17import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
18 19
@@ -25,12 +26,14 @@ import java.util.zip.GZIPOutputStream;
25 26
26public class MappingsRenamer { 27public class MappingsRenamer {
27 28
28 private JarIndex index; 29 private final JarIndex index;
30 private final ReferencedEntryPool entryPool;
29 private Mappings mappings; 31 private Mappings mappings;
30 32
31 public MappingsRenamer(JarIndex index, Mappings mappings) { 33 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
32 this.index = index; 34 this.index = index;
33 this.mappings = mappings; 35 this.mappings = mappings;
36 this.entryPool = entryPool;
34 } 37 }
35 38
36 public void setMappings(Mappings mappings) { 39 public void setMappings(Mappings mappings) {
@@ -46,7 +49,7 @@ public class MappingsRenamer {
46 49
47 if (deobfName != null) { 50 if (deobfName != null) {
48 // make sure we don't rename to an existing obf or deobf class 51 // make sure we don't rename to an existing obf or deobf class
49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { 52 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
50 throw new IllegalNameException(deobfName, "There is already a class with that name"); 53 throw new IllegalNameException(deobfName, "There is already a class with that name");
51 } 54 }
52 } 55 }
@@ -87,13 +90,13 @@ public class MappingsRenamer {
87 90
88 public void setFieldName(FieldEntry obf, String deobfName) { 91 public void setFieldName(FieldEntry obf, String deobfName) {
89 deobfName = NameValidator.validateFieldName(deobfName); 92 deobfName = NameValidator.validateFieldName(deobfName);
90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 93 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
91 ClassEntry definedClass = null; 94 ClassEntry definedClass = null;
92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) 95 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry))
93 definedClass = obf.getClassEntry(); 96 definedClass = obf.getOwnerClassEntry();
94 else { 97 else {
95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { 98 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { 99 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
97 definedClass = ancestorEntry; 100 definedClass = ancestorEntry;
98 break; 101 break;
99 } 102 }
@@ -101,42 +104,44 @@ public class MappingsRenamer {
101 } 104 }
102 105
103 if (definedClass != null) { 106 if (definedClass != null) {
104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); 107 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
108 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
105 if (className == null) 109 if (className == null)
106 className = definedClass.getClassName(); 110 className = definedClass.getClassName();
107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); 111 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
108 } 112 }
109 113
110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 114 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); 115 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
112 } 116 }
113 117
114 public void removeFieldMapping(FieldEntry obf) { 118 public void removeFieldMapping(FieldEntry obf) {
115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 119 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); 120 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
117 } 121 }
118 122
119 public void markFieldAsDeobfuscated(FieldEntry obf) { 123 public void markFieldAsDeobfuscated(FieldEntry obf) {
120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 124 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); 125 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
122 } 126 }
123 127
124 private void validateMethodTreeName(MethodEntry entry, String deobfName) { 128 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); 129 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
126 130
127 // TODO: Verify if I don't break things 131 // TODO: Verify if I don't break things
128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 132 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())) 133 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
130 || index.containsObfBehavior(targetEntry)) { 134 || index.containsObfMethod(targetEntry)) {
131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); 135 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
136 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
132 if (deobfClassName == null) { 137 if (deobfClassName == null) {
133 deobfClassName = entry.getClassName(); 138 deobfClassName = entry.getClassName();
134 } 139 }
135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 140 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
136 } 141 }
137 142
138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { 143 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName); 144 validateMethodTreeName(entry.updateOwnership(child), deobfName);
140 } 145 }
141 } 146 }
142 147
@@ -155,20 +160,21 @@ public class MappingsRenamer {
155 160
156 public void setMethodName(MethodEntry obf, String deobfName) { 161 public void setMethodName(MethodEntry obf, String deobfName) {
157 deobfName = NameValidator.validateMethodName(deobfName); 162 deobfName = NameValidator.validateMethodName(deobfName);
158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 163 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 164 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
160 165
161 // TODO: Verify if I don't break things 166 // 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())) 167 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
163 || index.containsObfBehavior(targetEntry)) { 168 || index.containsObfMethod(targetEntry)) {
164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); 169 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
170 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
165 if (deobfClassName == null) { 171 if (deobfClassName == null) {
166 deobfClassName = obf.getClassName(); 172 deobfClassName = obf.getClassName();
167 } 173 }
168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 174 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
169 } 175 }
170 176
171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); 177 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
172 } 178 }
173 179
174 public void removeMethodTreeMapping(MethodEntry obf) { 180 public void removeMethodTreeMapping(MethodEntry obf) {
@@ -176,8 +182,8 @@ public class MappingsRenamer {
176 } 182 }
177 183
178 public void removeMethodMapping(MethodEntry obf) { 184 public void removeMethodMapping(MethodEntry obf) {
179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 185 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null); 186 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
181 } 187 }
182 188
183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) { 189 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
@@ -185,30 +191,25 @@ public class MappingsRenamer {
185 } 191 }
186 192
187 public void markMethodAsDeobfuscated(MethodEntry obf) { 193 public void markMethodAsDeobfuscated(MethodEntry obf) {
188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 194 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); 195 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
190 } 196 }
191 197
192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { 198 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { 199 MethodEntry obfMethod = obf.getOwnerEntry();
194 setArgumentName(obf, deobfName);
195 return;
196 }
197
198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry();
199 200
200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); 201 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
201 for (MethodEntry entry : implementations) { 202 for (MethodEntry entry : implementations) {
202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 203 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
203 if (classMapping != null) { 204 if (classMapping != null) {
204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); 205 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 206 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
206 // TODO: Verify if I don't break things 207 // TODO: Verify if I don't break things
207 if (mapping != null) { 208 if (mapping != null) {
208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 209 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
209 if (argumentMapping.getIndex() != obf.getIndex()) { 210 if (localVariableMapping.getIndex() != obf.getIndex()) {
210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 211 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
211 || argumentMapping.getName().equals(deobfName)) { 212 || localVariableMapping.getName().equals(deobfName)) {
212 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 213 throw new IllegalNameException(deobfName, "There is already an argument with that name");
213 } 214 }
214 } 215 }
@@ -218,45 +219,45 @@ public class MappingsRenamer {
218 } 219 }
219 220
220 for (MethodEntry entry : implementations) { 221 for (MethodEntry entry : implementations) {
221 setArgumentName(new ArgumentEntry(obf, entry), deobfName); 222 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName()), deobfName);
222 } 223 }
223 } 224 }
224 225
225 public void setArgumentName(ArgumentEntry obf, String deobfName) { 226 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
226 deobfName = NameValidator.validateArgumentName(deobfName); 227 deobfName = NameValidator.validateArgumentName(deobfName);
227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 228 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); 229 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 230 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
230 // TODO: Verify if I don't break things 231 // TODO: Verify if I don't break things
231 if (mapping != null) { 232 if (mapping != null) {
232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 233 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
233 if (argumentMapping.getIndex() != obf.getIndex()) { 234 if (localVariableMapping.getIndex() != obf.getIndex()) {
234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 235 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
235 || argumentMapping.getName().equals(deobfName)) { 236 || localVariableMapping.getName().equals(deobfName)) {
236 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 237 throw new IllegalNameException(deobfName, "There is already an argument with that name");
237 } 238 }
238 } 239 }
239 } 240 }
240 } 241 }
241 242
242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); 243 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
243 } 244 }
244 245
245 public void removeArgumentMapping(ArgumentEntry obf) { 246 public void removeLocalVariableMapping(LocalVariableEntry obf) {
246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 247 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); 248 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
248 } 249 }
249 250
250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) { 251 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 252 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 253 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
253 } 254 }
254 255
255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { 256 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
256 classMapping.removeFieldMapping(fieldMapping); 257 classMapping.removeFieldMapping(fieldMapping);
257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 258 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { 259 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { 260 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
260 targetClassMapping.addFieldMapping(fieldMapping); 261 targetClassMapping.addFieldMapping(fieldMapping);
261 return true; 262 return true;
262 } else { 263 } else {
@@ -269,12 +270,12 @@ public class MappingsRenamer {
269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { 270 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
270 classMapping.removeMethodMapping(methodMapping); 271 classMapping.removeMethodMapping(methodMapping);
271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 272 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { 273 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { 274 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
274 targetClassMapping.addMethodMapping(methodMapping); 275 targetClassMapping.addMethodMapping(methodMapping);
275 return true; 276 return true;
276 } else { 277 } else {
277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); 278 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
278 } 279 }
279 } 280 }
280 return false; 281 return false;
@@ -326,12 +327,35 @@ public class MappingsRenamer {
326 } 327 }
327 328
328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { 329 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 330 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); 331 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
332 }
333
334 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
335 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
336 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
337 }
338
339 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
340 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
341 return classMapping.getModifier();
331 } 342 }
332 343
333 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { 344 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 345 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); 346 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
347 if (fieldMapping == null) {
348 return Mappings.EntryModifier.UNCHANGED;
349 }
350 return fieldMapping.getModifier();
351 }
352
353 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
354 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
355 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
356 if (methodMapping == null) {
357 return Mappings.EntryModifier.UNCHANGED;
358 }
359 return methodMapping.getModifier();
336 } 360 }
337} 361}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index b0eb826e..32f0ee9f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -2,6 +2,7 @@ package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Charsets; 3import com.google.common.base.Charsets;
4import cuchaz.enigma.analysis.TranslationIndex; 4import cuchaz.enigma.analysis.TranslationIndex;
5import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
5 6
6import java.io.*; 7import java.io.*;
7import java.util.ArrayList; 8import java.util.ArrayList;
@@ -19,7 +20,7 @@ public class MappingsSRGWriter {
19 } 20 }
20 file.createNewFile(); 21 file.createNewFile();
21 22
22 TranslationIndex index = new TranslationIndex(); 23 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
23 24
24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); 25 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
25 List<String> fieldMappings = new ArrayList<>(); 26 List<String> fieldMappings = new ArrayList<>();
@@ -43,7 +44,7 @@ public class MappingsSRGWriter {
43 } 44 }
44 45
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 46 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())); 47 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
47 } 48 }
48 } 49 }
49 50
@@ -52,7 +53,7 @@ public class MappingsSRGWriter {
52 } 53 }
53 54
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 55 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())); 56 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
56 } 57 }
57 } 58 }
58 for (String fd : fieldMappings) { 59 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 befc92ab..69d5684b 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
@@ -2,6 +2,7 @@ package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Charsets; 3import com.google.common.base.Charsets;
4import com.google.common.collect.Maps; 4import com.google.common.collect.Maps;
5import cuchaz.enigma.mapping.entry.ClassEntry;
5import cuchaz.enigma.throwables.MappingConflict; 6import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException; 7import cuchaz.enigma.throwables.MappingParseException;
7 8
@@ -20,11 +21,11 @@ public class MappingsTinyReader {
20 } 21 }
21 22
22 public FieldMapping readField(String[] parts) { 23 public FieldMapping readField(String[] parts) {
23 return new FieldMapping(parts[3], new Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); 24 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
24 } 25 }
25 26
26 public MethodMapping readMethod(String[] parts) { 27 public MethodMapping readMethod(String[] parts) {
27 return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); 28 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
28 } 29 }
29 30
30 public Mappings read(File file) throws IOException, MappingParseException { 31 public Mappings read(File file) throws IOException, MappingParseException {
@@ -72,7 +73,7 @@ public class MappingsTinyReader {
72 break; 73 break;
73 case "MTH-ARG": 74 case "MTH-ARG":
74 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); 75 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
75 classMapping.setArgumentName(parts[3], new Signature(parts[2]), Integer.parseInt(parts[4]), parts[5]); 76 classMapping.setArgumentName(parts[3], new MethodDescriptor(parts[2]), Integer.parseInt(parts[4]), parts[5]);
76 break; 77 break;
77 default: 78 default:
78 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !"); 79 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !");
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
index d4514d42..6effb91f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
@@ -11,6 +11,9 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.Entry;
16
14public interface MemberMapping<T extends Entry> { 17public interface MemberMapping<T extends Entry> {
15 T getObfEntry(ClassEntry classEntry); 18 T getObfEntry(ClassEntry classEntry);
16 19
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..0fc03517
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
@@ -0,0 +1,114 @@
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.mapping.entry.ClassEntry;
16import cuchaz.enigma.utils.Utils;
17
18import java.util.ArrayList;
19import java.util.List;
20import java.util.function.Function;
21
22public class MethodDescriptor {
23
24 private List<TypeDescriptor> argumentDescs;
25 private TypeDescriptor returnDesc;
26
27 public MethodDescriptor(String desc) {
28 try {
29 this.argumentDescs = Lists.newArrayList();
30 int i = 0;
31 while (i < desc.length()) {
32 char c = desc.charAt(i);
33 if (c == '(') {
34 assert (this.argumentDescs.isEmpty());
35 assert (this.returnDesc == null);
36 i++;
37 } else if (c == ')') {
38 i++;
39 break;
40 } else {
41 String type = TypeDescriptor.parseFirst(desc.substring(i));
42 this.argumentDescs.add(new TypeDescriptor(type));
43 i += type.length();
44 }
45 }
46 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
47 } catch (Exception ex) {
48 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
49 }
50 }
51
52 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
53 this.argumentDescs = argumentDescs;
54 this.returnDesc = returnDesc;
55 }
56
57 public List<TypeDescriptor> getArgumentDescs() {
58 return this.argumentDescs;
59 }
60
61 public TypeDescriptor getReturnDesc() {
62 return this.returnDesc;
63 }
64
65 @Override
66 public String toString() {
67 StringBuilder buf = new StringBuilder();
68 buf.append("(");
69 for (TypeDescriptor desc : this.argumentDescs) {
70 buf.append(desc);
71 }
72 buf.append(")");
73 buf.append(this.returnDesc);
74 return buf.toString();
75 }
76
77 public Iterable<TypeDescriptor> types() {
78 List<TypeDescriptor> descs = Lists.newArrayList();
79 descs.addAll(this.argumentDescs);
80 descs.add(this.returnDesc);
81 return descs;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
87 }
88
89 public boolean equals(MethodDescriptor other) {
90 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
91 }
92
93 @Override
94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
96 }
97
98 public boolean hasClass(ClassEntry classEntry) {
99 for (TypeDescriptor desc : types()) {
100 if (desc.containsType() && desc.getTypeEntry().equals(classEntry)) {
101 return true;
102 }
103 }
104 return false;
105 }
106
107 public MethodDescriptor remap(Function<String, String> remapper) {
108 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
109 for (TypeDescriptor desc : this.argumentDescs) {
110 argumentDescs.add(desc.remap(remapper));
111 }
112 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
113 }
114}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
deleted file mode 100644
index 9c3058c4..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ /dev/null
@@ -1,90 +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 MethodEntry implements BehaviorEntry {
17
18 private ClassEntry classEntry;
19 private String name;
20 private Signature signature;
21
22 public MethodEntry(ClassEntry classEntry, String name, Signature signature) {
23 if (classEntry == null) {
24 throw new IllegalArgumentException("Class cannot be null!");
25 }
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
36 this.classEntry = classEntry;
37 this.name = name;
38 this.signature = signature;
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 }
46
47 @Override
48 public ClassEntry getClassEntry() {
49 return this.classEntry;
50 }
51
52 @Override
53 public String getName() {
54 return this.name;
55 }
56
57 @Override
58 public Signature getSignature() {
59 return this.signature;
60 }
61
62 @Override
63 public String getClassName() {
64 return this.classEntry.getName();
65 }
66
67 @Override
68 public MethodEntry cloneToNewClass(ClassEntry classEntry) {
69 return new MethodEntry(this, classEntry.getName());
70 }
71
72 @Override
73 public int hashCode() {
74 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature);
75 }
76
77 @Override
78 public boolean equals(Object other) {
79 return other instanceof MethodEntry && equals((MethodEntry) other);
80 }
81
82 public boolean equals(MethodEntry other) {
83 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature);
84 }
85
86 @Override
87 public String toString() {
88 return this.classEntry.getName() + "." + this.name + this.signature;
89 }
90}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index 1524ce63..2f10144e 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -11,50 +11,49 @@
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;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
15import cuchaz.enigma.throwables.IllegalNameException; 18import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.throwables.MappingConflict; 19import cuchaz.enigma.throwables.MappingConflict;
17 20
18import java.util.Map; 21import java.util.Map;
19 22
20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 23public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
21 24
22 private String obfName; 25 private String obfName;
23 private String deobfName; 26 private String deobfName;
24 private Signature obfSignature; 27 private MethodDescriptor obfDescriptor;
25 private Map<Integer, ArgumentMapping> arguments; 28 private Map<Integer, LocalVariableMapping> localVariables;
26 private Mappings.EntryModifier modifier; 29 private Mappings.EntryModifier modifier;
27 30
28 public MethodMapping(String obfName, Signature obfSignature) { 31 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
29 this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); 32 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
30 } 33 }
31 34
32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 35 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); 36 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
34 } 37 }
35 38
36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { 39 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
37 if (obfName == null) { 40 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
38 throw new IllegalArgumentException("obf name cannot be null!"); 41 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; 42 this.obfName = obfName;
44 this.deobfName = NameValidator.validateMethodName(deobfName); 43 this.deobfName = NameValidator.validateMethodName(deobfName);
45 this.obfSignature = obfSignature; 44 this.obfDescriptor = obfDescriptor;
46 this.arguments = Maps.newTreeMap(); 45 this.localVariables = Maps.newTreeMap();
47 this.modifier = modifier; 46 this.modifier = modifier;
48 } 47 }
49 48
50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 49 public MethodMapping(MethodMapping other, Translator translator) {
51 this.obfName = other.obfName; 50 this.obfName = other.obfName;
52 this.deobfName = other.deobfName; 51 this.deobfName = other.deobfName;
53 this.modifier = other.modifier; 52 this.modifier = other.modifier;
54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 53 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
55 this.arguments = Maps.newTreeMap(); 54 this.localVariables = Maps.newTreeMap();
56 for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) { 55 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); 56 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
58 } 57 }
59 } 58 }
60 59
@@ -77,6 +76,9 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
77 } 76 }
78 77
79 public String getDeobfName() { 78 public String getDeobfName() {
79 if (deobfName == null) {
80 return obfName;
81 }
80 return this.deobfName; 82 return this.deobfName;
81 } 83 }
82 84
@@ -84,56 +86,56 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
84 this.deobfName = NameValidator.validateMethodName(val); 86 this.deobfName = NameValidator.validateMethodName(val);
85 } 87 }
86 88
87 public Signature getObfSignature() { 89 public MethodDescriptor getObfDesc() {
88 return this.obfSignature; 90 return this.obfDescriptor;
89 } 91 }
90 92
91 public void setObfSignature(Signature val) { 93 public void setObfDescriptor(MethodDescriptor val) {
92 this.obfSignature = val; 94 this.obfDescriptor = val;
93 } 95 }
94 96
95 public Iterable<ArgumentMapping> arguments() { 97 public Iterable<LocalVariableMapping> arguments() {
96 return this.arguments.values(); 98 return this.localVariables.values();
97 } 99 }
98 100
99 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { 101 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
100 if (this.arguments.containsKey(argumentMapping.getIndex())) { 102 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
101 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); 103 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
102 } 104 }
103 this.arguments.put(argumentMapping.getIndex(), argumentMapping); 105 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
104 } 106 }
105 107
106 public String getObfArgumentName(int index) { 108 public String getObfLocalVariableName(int index) {
107 ArgumentMapping argumentMapping = this.arguments.get(index); 109 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
108 if (argumentMapping != null) { 110 if (localVariableMapping != null) {
109 return argumentMapping.getName(); 111 return localVariableMapping.getName();
110 } 112 }
111 113
112 return null; 114 return null;
113 } 115 }
114 116
115 public String getDeobfArgumentName(int index) { 117 public String getDeobfLocalVariableName(int index) {
116 ArgumentMapping argumentMapping = this.arguments.get(index); 118 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
117 if (argumentMapping != null) { 119 if (localVariableMapping != null) {
118 return argumentMapping.getName(); 120 return localVariableMapping.getName();
119 } 121 }
120 122
121 return null; 123 return null;
122 } 124 }
123 125
124 public void setArgumentName(int index, String name) { 126 public void setLocalVariableName(int index, String name) {
125 ArgumentMapping argumentMapping = this.arguments.get(index); 127 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
126 if (argumentMapping == null) { 128 if (localVariableMapping == null) {
127 argumentMapping = new ArgumentMapping(index, name); 129 localVariableMapping = new LocalVariableMapping(index, name);
128 boolean wasAdded = this.arguments.put(index, argumentMapping) == null; 130 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
129 assert (wasAdded); 131 assert (wasAdded);
130 } else { 132 } else {
131 argumentMapping.setName(name); 133 localVariableMapping.setName(name);
132 } 134 }
133 } 135 }
134 136
135 public void removeArgumentName(int index) { 137 public void removeLocalVariableName(int index) {
136 boolean wasRemoved = this.arguments.remove(index) != null; 138 boolean wasRemoved = this.localVariables.remove(index) != null;
137 assert (wasRemoved); 139 assert (wasRemoved);
138 } 140 }
139 141
@@ -146,14 +148,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
146 buf.append(this.deobfName); 148 buf.append(this.deobfName);
147 buf.append("\n"); 149 buf.append("\n");
148 buf.append("\t"); 150 buf.append("\t");
149 buf.append(this.obfSignature); 151 buf.append(this.obfDescriptor);
150 buf.append("\n"); 152 buf.append("\n");
151 buf.append("\tArguments:\n"); 153 buf.append("\tLocal Variables:\n");
152 for (ArgumentMapping argumentMapping : this.arguments.values()) { 154 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
153 buf.append("\t\t"); 155 buf.append("\t\t");
154 buf.append(argumentMapping.getIndex()); 156 buf.append(localVariableMapping.getIndex());
155 buf.append(" -> "); 157 buf.append(" -> ");
156 buf.append(argumentMapping.getName()); 158 buf.append(localVariableMapping.getName());
157 buf.append("\n"); 159 buf.append("\n");
158 } 160 }
159 return buf.toString(); 161 return buf.toString();
@@ -161,12 +163,12 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
161 163
162 @Override 164 @Override
163 public int compareTo(MethodMapping other) { 165 public int compareTo(MethodMapping other) {
164 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); 166 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
165 } 167 }
166 168
167 public boolean containsArgument(String name) { 169 public boolean containsLocalVariable(String name) {
168 for (ArgumentMapping argumentMapping : this.arguments.values()) { 170 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
169 if (argumentMapping.getName().equals(name)) { 171 if (localVariableMapping.getName().equals(name)) {
170 return true; 172 return true;
171 } 173 }
172 } 174 }
@@ -175,32 +177,23 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
175 177
176 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 178 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
177 // rename obf classes in the signature 179 // rename obf classes in the signature
178 Signature newSignature = new Signature(this.obfSignature, className -> 180 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
179 {
180 if (className.equals(oldObfClassName)) { 181 if (className.equals(oldObfClassName)) {
181 return newObfClassName; 182 return newObfClassName;
182 } 183 }
183 return null; 184 return className;
184 }); 185 });
185 186
186 if (!newSignature.equals(this.obfSignature)) { 187 if (!newDescriptor.equals(this.obfDescriptor)) {
187 this.obfSignature = newSignature; 188 this.obfDescriptor = newDescriptor;
188 return true; 189 return true;
189 } 190 }
190 return false; 191 return false;
191 } 192 }
192 193
193 public boolean isConstructor() {
194 return this.obfName.startsWith("<");
195 }
196
197 @Override 194 @Override
198 public BehaviorEntry getObfEntry(ClassEntry classEntry) { 195 public MethodEntry getObfEntry(ClassEntry classEntry) {
199 if (isConstructor()) { 196 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 } 197 }
205 198
206 public Mappings.EntryModifier getModifier() { 199 public Mappings.EntryModifier getModifier() {
@@ -210,4 +203,8 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
210 public void setModifier(Mappings.EntryModifier modifier) { 203 public void setModifier(Mappings.EntryModifier modifier) {
211 this.modifier = modifier; 204 this.modifier = modifier;
212 } 205 }
206
207 public boolean isObfuscated() {
208 return deobfName == null || deobfName.equals(obfName);
209 }
213} 210}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index aa3dc4de..9273c9bc 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -11,8 +11,8 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
14import cuchaz.enigma.throwables.IllegalNameException; 15import cuchaz.enigma.throwables.IllegalNameException;
15import javassist.bytecode.Descriptor;
16 16
17import java.util.Arrays; 17import java.util.Arrays;
18import java.util.List; 18import java.util.List;
@@ -23,11 +23,11 @@ public class NameValidator {
23 private static final Pattern IdentifierPattern; 23 private static final Pattern IdentifierPattern;
24 private static final Pattern ClassPattern; 24 private static final Pattern ClassPattern;
25 private static final List<String> ReservedWords = Arrays.asList( 25 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", 29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while" 30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
31 ); 31 );
32 32
33 static { 33 static {
@@ -43,10 +43,10 @@ public class NameValidator {
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { 43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 44 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 } 45 }
46 if (packageRequired && new ClassEntry(name).getPackageName() == null) { 46 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package"); 47 throw new IllegalNameException(name, "Class must be in a package");
48 } 48 }
49 return Descriptor.toJvmName(name); 49 return name;
50 } 50 }
51 51
52 public static String validateFieldName(String name) { 52 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
deleted file mode 100644
index 33d930dc..00000000
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ /dev/null
@@ -1,67 +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.strobel.assembler.metadata.*;
15
16import java.util.List;
17
18public class ProcyonEntryFactory {
19
20 private static String getErasedSignature(MemberReference def) {
21 if (!(def instanceof MethodReference))
22 return def.getErasedSignature();
23 MethodReference methodReference = (MethodReference) def;
24 StringBuilder builder = new StringBuilder("(");
25 for (ParameterDefinition param : methodReference.getParameters()) {
26 TypeReference paramType = param.getParameterType();
27 if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) {
28 List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces();
29 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
30 } else
31 builder.append(paramType.getErasedSignature());
32 }
33 builder.append(")");
34
35 TypeReference returnType = methodReference.getReturnType();
36 if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) {
37 List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces();
38 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
39 } else
40 builder.append(returnType.getErasedSignature());
41 return builder.toString();
42 }
43
44 public static FieldEntry getFieldEntry(MemberReference def) {
45 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature()));
46 }
47
48 public static MethodEntry getMethodEntry(MemberReference def) {
49 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def)));
50 }
51
52 public static ConstructorEntry getConstructorEntry(MethodReference def) {
53 if (def.isTypeInitializer()) {
54 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()));
55 } else {
56 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
57 }
58 }
59
60 public static BehaviorEntry getBehaviorEntry(MethodReference def) {
61 if (def.isConstructor() || def.isTypeInitializer()) {
62 return getConstructorEntry(def);
63 } else {
64 return getMethodEntry(def);
65 }
66 }
67}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
index 78130d6b..071e4afa 100644
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ b/src/main/java/cuchaz/enigma/mapping/Signature.java
@@ -1,106 +1,82 @@
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; 1package cuchaz.enigma.mapping;
13 2
14import com.google.common.collect.Lists; 3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
15import cuchaz.enigma.utils.Utils; 4import org.objectweb.asm.signature.SignatureReader;
5import org.objectweb.asm.signature.SignatureVisitor;
6import org.objectweb.asm.signature.SignatureWriter;
16 7
17import java.util.List; 8import java.util.function.Function;
9import java.util.regex.Pattern;
18 10
19public class Signature { 11public class Signature {
12 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
20 13
21 private List<Type> argumentTypes; 14 private final String signature;
22 private Type returnType; 15 private final boolean isType;
23 16
24 public Signature(String signature) { 17 private Signature(String signature, boolean isType) {
25 try { 18 if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) {
26 this.argumentTypes = Lists.newArrayList(); 19 signature = signature.replaceAll(":Ljava/lang/Object;:", "::");
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 } 20 }
21
22 this.signature = signature;
23 this.isType = isType;
47 } 24 }
48 25
49 public Signature(Signature other, ClassNameReplacer replacer) { 26 public static Signature createTypedSignature(String signature) {
50 this.argumentTypes = Lists.newArrayList(other.argumentTypes); 27 if (signature != null && !signature.isEmpty()) {
51 for (int i = 0; i < this.argumentTypes.size(); i++) { 28 return new Signature(signature, true);
52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
53 } 29 }
54 this.returnType = new Type(other.returnType, replacer); 30 return new Signature(null, true);
55 } 31 }
56 32
57 public List<Type> getArgumentTypes() { 33 public static Signature createSignature(String signature) {
58 return this.argumentTypes; 34 if (signature != null && !signature.isEmpty()) {
35 return new Signature(signature, false);
36 }
37 return new Signature(null, false);
59 } 38 }
60 39
61 public Type getReturnType() { 40 public String getSignature() {
62 return this.returnType; 41 return signature;
63 } 42 }
64 43
65 @Override 44 public boolean isType() {
66 public String toString() { 45 return isType;
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 } 46 }
76 47
77 public Iterable<Type> types() { 48 public Signature remap(Function<String, String> remapper) {
78 List<Type> types = Lists.newArrayList(); 49 if (signature == null) {
79 types.addAll(this.argumentTypes); 50 return this;
80 types.add(this.returnType); 51 }
81 return types; 52 SignatureWriter writer = new SignatureWriter();
53 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer);
54 if (isType) {
55 new SignatureReader(signature).acceptType(visitor);
56 } else {
57 new SignatureReader(signature).accept(visitor);
58 }
59 return new Signature(writer.toString(), isType);
82 } 60 }
83 61
84 @Override 62 @Override
85 public boolean equals(Object other) { 63 public boolean equals(Object obj) {
86 return other instanceof Signature && equals((Signature) other); 64 if (obj instanceof Signature) {
87 } 65 Signature other = (Signature) obj;
88 66 return (other.signature == null && signature == null || other.signature != null
89 public boolean equals(Signature other) { 67 && signature != null && other.signature.equals(signature))
90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); 68 && other.isType == this.isType;
69 }
70 return false;
91 } 71 }
92 72
93 @Override 73 @Override
94 public int hashCode() { 74 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); 75 return signature.hashCode() | (isType ? 1 : 0) << 16;
96 } 76 }
97 77
98 public boolean hasClass(ClassEntry classEntry) { 78 @Override
99 for (Type type : types()) { 79 public String toString() {
100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) { 80 return signature;
101 return true;
102 }
103 }
104 return false;
105 } 81 }
106} 82}
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..a9ff1cbb 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -11,332 +11,99 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14import cuchaz.enigma.mapping.entry.*;
15import com.google.common.collect.Maps; 15import org.objectweb.asm.Handle;
16import cuchaz.enigma.analysis.TranslationIndex; 16import org.objectweb.asm.Type;
17 17
18import java.util.List; 18public interface Translator {
19import java.util.Map; 19 ClassEntry getTranslatedClass(ClassEntry entry);
20 20
21public class Translator { 21 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
22 22
23 private TranslationDirection direction; 23 FieldEntry getTranslatedField(FieldEntry entry);
24 private Map<String, ClassMapping> classes;
25 private TranslationIndex index;
26 24
27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 25 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
28 26
29 public Translator() { 27 MethodEntry getTranslatedMethod(MethodEntry entry);
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex();
33 }
34 28
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 29 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40 30
41 public TranslationDirection getDirection() { 31 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
42 return direction;
43 }
44 32
45 public TranslationIndex getTranslationIndex() { 33 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
46 return index;
47 }
48 34
49 @SuppressWarnings("unchecked") 35 boolean hasClassMapping(ClassEntry entry);
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 36
68 public <T extends Entry> String translate(T entry) { 37 boolean hasFieldMapping(FieldEntry entry);
69 if (entry instanceof ClassEntry) {
70 return translate((ClassEntry) entry);
71 } else if (entry instanceof FieldEntry) {
72 return translate((FieldEntry) entry);
73 } else if (entry instanceof MethodEntry) {
74 return translate((MethodEntry) entry);
75 } else if (entry instanceof ConstructorEntry) {
76 return translate(entry);
77 } else if (entry instanceof ArgumentEntry) {
78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry);
81 } else {
82 throw new Error("Unknown entry type: " + entry.getClass().getName());
83 }
84 }
85 38
86 public String translate(LocalVariableEntry in) { 39 boolean hasMethodMapping(MethodEntry entry);
87 LocalVariableEntry translated = translateEntry(in);
88 if (translated.equals(in)) {
89 return null;
90 }
91 return translated.getName();
92 }
93 40
94 public LocalVariableEntry translateEntry(LocalVariableEntry in) { 41 boolean hasLocalVariableMapping(LocalVariableEntry entry);
95 // TODO: Implement it
96 return in;
97 }
98 42
99 public String translate(ClassEntry in) { 43 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
100 ClassEntry translated = translateEntry(in);
101 if (translated.equals(in)) {
102 return null;
103 }
104 return translated.getName();
105 }
106 44
107 public String translateClass(String className) { 45 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
108 return translate(new ClassEntry(className));
109 }
110
111 public ClassEntry translateEntry(ClassEntry in) {
112 46
113 if (in.isInnerClass()) { 47 Signature getTranslatedSignature(Signature signature);
114 48
115 // translate as much of the class chain as we can 49 default Type getTranslatedType(Type type) {
116 List<ClassMapping> mappingsChain = getClassMappingChain(in); 50 String descString = type.getDescriptor();
117 String[] obfClassNames = in.getName().split("\\$"); 51 switch (type.getSort()) {
118 StringBuilder buf = new StringBuilder(); 52 case Type.OBJECT: {
119 for (int i = 0; i < obfClassNames.length; i++) { 53 ClassEntry classEntry = new ClassEntry(type.getInternalName());
120 boolean isFirstClass = buf.length() == 0; 54 return Type.getObjectType(getTranslatedClass(classEntry).getName());
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 } 55 }
137 return new ClassEntry(buf.toString()); 56 case Type.ARRAY: {
138 57 TypeDescriptor descriptor = new TypeDescriptor(descString);
139 } else { 58 return Type.getType(getTranslatedTypeDesc(descriptor).toString());
140
141 // normal classes are easy
142 ClassMapping classMapping = this.classes.get(in.getName());
143 if (classMapping == null) {
144 return in;
145 } 59 }
146 return this.direction.choose( 60 case Type.METHOD: {
147 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, 61 MethodDescriptor descriptor = new MethodDescriptor(descString);
148 new ClassEntry(classMapping.getObfFullName()) 62 return Type.getMethodType(getTranslatedMethodDesc(descriptor).toString());
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 } 63 }
172 } 64 }
173 return null; 65 return type;
174 } 66 }
175 67
176 public FieldEntry translateEntry(FieldEntry in) { 68 default Handle getTranslatedHandle(Handle handle) {
177 String name = translate(in); 69 MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc()));
178 if (name == null) { 70 MethodEntry translatedMethod = getTranslatedMethod(entry);
179 name = in.getName(); 71 ClassEntry ownerClass = translatedMethod.getOwnerClassEntry();
180 } 72 return new Handle(handle.getTag(), ownerClass.getName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface());
181 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType()));
182 } 73 }
183 74
184 public String translate(MethodEntry in) { 75 default Object getTranslatedValue(Object value) {
185 // resolve the class entry 76 if (value instanceof Type) {
186 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); 77 return this.getTranslatedType((Type) value);
187 if (resolvedClassEntry != null) { 78 } else if (value instanceof Handle) {
188 79 return getTranslatedHandle((Handle) value);
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 } 80 }
203 return null; 81 return value;
204 } 82 }
205 83
206 public MethodEntry translateEntry(MethodEntry in) { 84 @SuppressWarnings("unchecked")
207 String name = translate(in); 85 default <T extends Entry> T getTranslatedEntry(T entry) {
208 if (name == null) { 86 if (entry instanceof ClassDefEntry) {
209 name = in.getName(); 87 return (T) getTranslatedClassDef((ClassDefEntry) entry);
210 } 88 } else if (entry instanceof ClassEntry) {
211 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); 89 return (T) getTranslatedClass((ClassEntry) entry);
212 } 90 } else if (entry instanceof FieldDefEntry) {
213 91 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
214 public ConstructorEntry translateEntry(ConstructorEntry in) { 92 } else if (entry instanceof MethodDefEntry) {
215 if (in.isStatic()) { 93 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
216 return new ConstructorEntry(translateEntry(in.getClassEntry())); 94 } else if (entry instanceof FieldEntry) {
217 } else { 95 return (T) getTranslatedField((FieldEntry) entry);
218 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); 96 } else if (entry instanceof MethodEntry) {
219 } 97 return (T) getTranslatedMethod((MethodEntry) entry);
220 } 98 } else if (entry instanceof LocalVariableDefEntry) {
221 99 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
222 public BehaviorEntry translateEntry(BehaviorEntry in) { 100 } else if (entry instanceof LocalVariableEntry) {
223 if (in instanceof MethodEntry) { 101 return (T) getTranslatedVariable((LocalVariableEntry) entry);
224 return translateEntry((MethodEntry) in); 102 } else if (entry instanceof TypeDescriptor) {
225 } else if (in instanceof ConstructorEntry) { 103 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
226 return translateEntry((ConstructorEntry) in); 104 } else if (entry instanceof MethodDescriptor) {
227 } 105 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
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 } 106 }
340 return Mappings.EntryModifier.UNCHANGED; 107 throw new IllegalArgumentException("Cannot translate unknown entry type");
341 } 108 }
342} 109}
diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
index 609bd64e..b7b1255a 100644
--- a/src/main/java/cuchaz/enigma/mapping/Type.java
+++ b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
@@ -11,47 +11,36 @@
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;
16import cuchaz.enigma.mapping.entry.ClassEntry;
15 17
16import java.util.Map; 18import java.util.Map;
19import java.util.function.Function;
17 20
18public class Type { 21public class TypeDescriptor {
19 22
20 protected String name; 23 protected final String desc;
21 24
22 public Type(String name) { 25 public TypeDescriptor(String desc) {
26 Preconditions.checkNotNull(desc, "Desc cannot be null");
23 27
24 // don't deal with generics 28 // don't deal with generics
25 // this is just for raw jvm types 29 // this is just for raw jvm types
26 if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { 30 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
27 throw new IllegalArgumentException("don't use with generic types or templates: " + name); 31 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
28 } 32 }
29 33
30 this.name = name; 34 this.desc = desc;
31 }
32
33 public Type(Type other, ClassNameReplacer replacer) {
34 this.name = other.name;
35 if (other.isClass()) {
36 String replacedName = replacer.replace(other.getClassEntry().getClassName());
37 if (replacedName != null) {
38 this.name = "L" + replacedName + ";";
39 }
40 } else if (other.isArray() && other.hasClass()) {
41 String replacedName = replacer.replace(other.getClassEntry().getClassName());
42 if (replacedName != null) {
43 this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";";
44 }
45 }
46 } 35 }
47 36
48 public static String parseFirst(String in) { 37 public static String parseFirst(String in) {
49 38
50 if (in == null || in.length() <= 0) { 39 if (in == null || in.length() <= 0) {
51 throw new IllegalArgumentException("No type to parse, input is empty!"); 40 throw new IllegalArgumentException("No desc to parse, input is empty!");
52 } 41 }
53 42
54 // read one type from the input 43 // read one desc from the input
55 44
56 char c = in.charAt(0); 45 char c = in.charAt(0);
57 46
@@ -79,21 +68,13 @@ public class Type {
79 // then check for arrays 68 // then check for arrays
80 int dim = countArrayDimension(in); 69 int dim = countArrayDimension(in);
81 if (dim > 0) { 70 if (dim > 0) {
82 String arrayType = Type.parseFirst(in.substring(dim)); 71 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
83 return in.substring(0, dim + arrayType.length()); 72 return in.substring(0, dim + arrayType.length());
84 } 73 }
85 74
86 throw new IllegalArgumentException("don't know how to parse: " + in); 75 throw new IllegalArgumentException("don't know how to parse: " + in);
87 } 76 }
88 77
89 private static String getArrayPrefix(int dimension) {
90 StringBuilder buf = new StringBuilder();
91 for (int i = 0; i < dimension; i++) {
92 buf.append("[");
93 }
94 return buf.toString();
95 }
96
97 private static int countArrayDimension(String in) { 78 private static int countArrayDimension(String in) {
98 int i = 0; 79 int i = 0;
99 while (i < in.length() && in.charAt(i) == '[') 80 while (i < in.length() && in.charAt(i) == '[')
@@ -121,33 +102,37 @@ public class Type {
121 return null; 102 return null;
122 } 103 }
123 104
105 public static TypeDescriptor of(String name) {
106 return new TypeDescriptor("L" + name + ";");
107 }
108
124 @Override 109 @Override
125 public String toString() { 110 public String toString() {
126 return this.name; 111 return this.desc;
127 } 112 }
128 113
129 public boolean isVoid() { 114 public boolean isVoid() {
130 return this.name.length() == 1 && this.name.charAt(0) == 'V'; 115 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
131 } 116 }
132 117
133 public boolean isPrimitive() { 118 public boolean isPrimitive() {
134 return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; 119 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
135 } 120 }
136 121
137 public Primitive getPrimitive() { 122 public Primitive getPrimitive() {
138 if (!isPrimitive()) { 123 if (!isPrimitive()) {
139 throw new IllegalStateException("not a primitive"); 124 throw new IllegalStateException("not a primitive");
140 } 125 }
141 return Primitive.get(this.name.charAt(0)); 126 return Primitive.get(this.desc.charAt(0));
142 } 127 }
143 128
144 public boolean isClass() { 129 public boolean isType() {
145 return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; 130 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
146 } 131 }
147 132
148 public ClassEntry getClassEntry() { 133 public ClassEntry getTypeEntry() {
149 if (isClass()) { 134 if (isType()) {
150 String name = this.name.substring(1, this.name.length() - 1); 135 String name = this.desc.substring(1, this.desc.length() - 1);
151 136
152 int pos = name.indexOf('<'); 137 int pos = name.indexOf('<');
153 if (pos >= 0) { 138 if (pos >= 0) {
@@ -157,46 +142,70 @@ public class Type {
157 142
158 return new ClassEntry(name); 143 return new ClassEntry(name);
159 144
160 } else if (isArray() && getArrayType().isClass()) { 145 } else if (isArray() && getArrayType().isType()) {
161 return getArrayType().getClassEntry(); 146 return getArrayType().getTypeEntry();
162 } else { 147 } else {
163 throw new IllegalStateException("type doesn't have a class"); 148 throw new IllegalStateException("desc doesn't have a class");
164 } 149 }
165 } 150 }
166 151
167 public boolean isArray() { 152 public boolean isArray() {
168 return this.name.charAt(0) == '['; 153 return this.desc.charAt(0) == '[';
169 } 154 }
170 155
171 public int getArrayDimension() { 156 public int getArrayDimension() {
172 if (!isArray()) { 157 if (!isArray()) {
173 throw new IllegalStateException("not an array"); 158 throw new IllegalStateException("not an array");
174 } 159 }
175 return countArrayDimension(this.name); 160 return countArrayDimension(this.desc);
176 } 161 }
177 162
178 public Type getArrayType() { 163 public TypeDescriptor getArrayType() {
179 if (!isArray()) { 164 if (!isArray()) {
180 throw new IllegalStateException("not an array"); 165 throw new IllegalStateException("not an array");
181 } 166 }
182 return new Type(this.name.substring(getArrayDimension(), this.name.length())); 167 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length()));
183 } 168 }
184 169
185 public boolean hasClass() { 170 public boolean containsType() {
186 return isClass() || (isArray() && getArrayType().hasClass()); 171 return isType() || (isArray() && getArrayType().containsType());
187 } 172 }
188 173
189 @Override 174 @Override
190 public boolean equals(Object other) { 175 public boolean equals(Object other) {
191 return other instanceof Type && equals((Type) other); 176 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
192 } 177 }
193 178
194 public boolean equals(Type other) { 179 public boolean equals(TypeDescriptor other) {
195 return this.name.equals(other.name); 180 return this.desc.equals(other.desc);
196 } 181 }
197 182
183 @Override
198 public int hashCode() { 184 public int hashCode() {
199 return this.name.hashCode(); 185 return this.desc.hashCode();
186 }
187
188 public TypeDescriptor remap(Function<String, String> remapper) {
189 String desc = this.desc;
190 if (isType() || (isArray() && containsType())) {
191 String replacedName = remapper.apply(this.getTypeEntry().getName());
192 if (replacedName != null) {
193 if (this.isType()) {
194 desc = "L" + replacedName + ";";
195 } else {
196 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
197 }
198 }
199 }
200 return new TypeDescriptor(desc);
201 }
202
203 private static String getArrayPrefix(int dimension) {
204 StringBuilder buf = new StringBuilder();
205 for (int i = 0; i < dimension; i++) {
206 buf.append("[");
207 }
208 return buf.toString();
200 } 209 }
201 210
202 public enum Primitive { 211 public enum Primitive {
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
new file mode 100644
index 00000000..ac1fe2ab
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
@@ -0,0 +1,37 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17
18public class ClassDefEntry extends ClassEntry {
19 private final AccessFlags access;
20 private final Signature signature;
21
22 public ClassDefEntry(String className, Signature signature, AccessFlags access) {
23 super(className);
24 Preconditions.checkNotNull(signature, "Class signature cannot be null");
25 Preconditions.checkNotNull(access, "Class access cannot be null");
26 this.signature = signature;
27 this.access = access;
28 }
29
30 public Signature getSignature() {
31 return signature;
32 }
33
34 public AccessFlags getAccess() {
35 return access;
36 }
37}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
index 788811ff..c7958256 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
@@ -9,20 +9,20 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping.entry;
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
@@ -77,6 +77,10 @@ public class ClassEntry implements Entry {
77 return this.name; 77 return this.name;
78 } 78 }
79 79
80 public boolean isArray() {
81 return this.name.lastIndexOf('[') >= 0;
82 }
83
80 public boolean isInnerClass() { 84 public boolean isInnerClass() {
81 return this.name.lastIndexOf('$') >= 0; 85 return this.name.lastIndexOf('$') >= 0;
82 } 86 }
@@ -132,11 +136,7 @@ public class ClassEntry implements Entry {
132 } 136 }
133 137
134 public String getPackageName() { 138 public String getPackageName() {
135 int pos = this.name.lastIndexOf('/'); 139 return getPackageName(this.name);
136 if (pos > 0) {
137 return this.name.substring(0, pos);
138 }
139 return null;
140 } 140 }
141 141
142 public String getSimpleName() { 142 public String getSimpleName() {
@@ -147,6 +147,14 @@ public class ClassEntry implements Entry {
147 return this.name; 147 return this.name;
148 } 148 }
149 149
150 public static String getPackageName(String name) {
151 int pos = name.lastIndexOf('/');
152 if (pos > 0) {
153 return name.substring(0, pos);
154 }
155 return null;
156 }
157
150 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 158 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
151 assert (classChain.contains(this)); 159 assert (classChain.contains(this));
152 StringBuilder buf = new StringBuilder(); 160 StringBuilder buf = new StringBuilder();
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
index c79510b9..b612140f 100644
--- a/src/main/java/cuchaz/enigma/mapping/Entry.java
+++ b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
@@ -9,14 +9,14 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping.entry;
13 13
14public interface Entry { 14public interface Entry {
15 String getName(); 15 String getName();
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/entry/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
new file mode 100644
index 00000000..5bd159f4
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
@@ -0,0 +1,49 @@
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.entry;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.mapping.ClassMapping;
16import cuchaz.enigma.mapping.FieldMapping;
17import cuchaz.enigma.mapping.MethodDescriptor;
18import cuchaz.enigma.mapping.MethodMapping;
19
20public class EntryFactory {
21 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
22 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
23 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
24 }
25
26 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
27 return new ClassEntry(classMapping.getObfFullName());
28 }
29
30 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
31 return new ClassEntry(classMapping.getDeobfName());
32 }
33
34 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
35 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
36 }
37
38 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
39 return new MethodEntry(classEntry, name, desc);
40 }
41
42 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
43 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
44 }
45
46 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
47 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
new file mode 100644
index 00000000..d18115bf
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
@@ -0,0 +1,43 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17import cuchaz.enigma.mapping.TypeDescriptor;
18
19public class FieldDefEntry extends FieldEntry {
20 private final AccessFlags access;
21 private final Signature signature;
22
23 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
24 super(ownerEntry, name, desc);
25 Preconditions.checkNotNull(access, "Field access cannot be null");
26 Preconditions.checkNotNull(signature, "Field signature cannot be null");
27 this.access = access;
28 this.signature = signature;
29 }
30
31 public AccessFlags getAccess() {
32 return access;
33 }
34
35 public Signature getSignature() {
36 return signature;
37 }
38
39 @Override
40 public FieldDefEntry updateOwnership(ClassEntry owner) {
41 return new FieldDefEntry(owner, this.name, this.desc, signature, access);
42 }
43}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
new file mode 100644
index 00000000..b6e1554d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
@@ -0,0 +1,77 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.TypeDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class FieldEntry implements Entry {
19
20 protected final ClassEntry ownerEntry;
21 protected final String name;
22 protected final TypeDescriptor desc;
23
24 // NOTE: this argument order is important for the MethodReader/MethodWriter
25 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
26 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
27 Preconditions.checkNotNull(name, "Field name cannot be null");
28 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
29
30 this.ownerEntry = ownerEntry;
31 this.name = name;
32 this.desc = desc;
33 }
34
35 @Override
36 public ClassEntry getOwnerClassEntry() {
37 return this.ownerEntry;
38 }
39
40 @Override
41 public String getName() {
42 return this.name;
43 }
44
45 @Override
46 public String getClassName() {
47 return this.ownerEntry.getName();
48 }
49
50 public TypeDescriptor getDesc() {
51 return this.desc;
52 }
53
54 @Override
55 public FieldEntry updateOwnership(ClassEntry owner) {
56 return new FieldEntry(owner, this.name, this.desc);
57 }
58
59 @Override
60 public int hashCode() {
61 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
62 }
63
64 @Override
65 public boolean equals(Object other) {
66 return other instanceof FieldEntry && equals((FieldEntry) other);
67 }
68
69 public boolean equals(FieldEntry other) {
70 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
71 }
72
73 @Override
74 public String toString() {
75 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
new file mode 100644
index 00000000..77422720
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
@@ -0,0 +1,57 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableDefEntry extends LocalVariableEntry {
13
14 protected final MethodDefEntry ownerEntry;
15 protected final TypeDescriptor desc;
16
17 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
18 super(ownerEntry, index, name);
19 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
20
21 this.ownerEntry = ownerEntry;
22 this.desc = desc;
23 }
24
25 @Override
26 public MethodDefEntry getOwnerEntry() {
27 return this.ownerEntry;
28 }
29
30 public TypeDescriptor getDesc() {
31 return desc;
32 }
33
34 @Override
35 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
36 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, desc);
37 }
38
39 @Override
40 public int hashCode() {
41 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
42 }
43
44 @Override
45 public boolean equals(Object other) {
46 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
47 }
48
49 public boolean equals(LocalVariableDefEntry other) {
50 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
51 }
52
53 @Override
54 public String toString() {
55 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
56 }
57}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
new file mode 100644
index 00000000..a794d0a0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
@@ -0,0 +1,82 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableEntry implements Entry {
13
14 protected final MethodEntry ownerEntry;
15 protected final String name;
16 protected final int index;
17
18 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
19 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
20 Preconditions.checkNotNull(name, "Variable name cannot be null");
21 Preconditions.checkArgument(index >= 0, "Index must be positive");
22
23 this.ownerEntry = ownerEntry;
24 this.name = name;
25 this.index = index;
26 }
27
28 public MethodEntry getOwnerEntry() {
29 return this.ownerEntry;
30 }
31
32 public int getIndex() {
33 return index;
34 }
35
36 @Override
37 public String getName() {
38 return this.name;
39 }
40
41 @Override
42 public ClassEntry getOwnerClassEntry() {
43 return this.ownerEntry.getOwnerClassEntry();
44 }
45
46 @Override
47 public String getClassName() {
48 return this.ownerEntry.getClassName();
49 }
50
51 @Override
52 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
53 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name);
54 }
55
56 public String getMethodName() {
57 return this.ownerEntry.getName();
58 }
59
60 public MethodDescriptor getMethodDesc() {
61 return this.ownerEntry.getDesc();
62 }
63
64 @Override
65 public int hashCode() {
66 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
67 }
68
69 @Override
70 public boolean equals(Object other) {
71 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
72 }
73
74 public boolean equals(LocalVariableEntry other) {
75 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
76 }
77
78 @Override
79 public String toString() {
80 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
81 }
82}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
new file mode 100644
index 00000000..bb7c85eb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
@@ -0,0 +1,54 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.MethodDescriptor;
17import cuchaz.enigma.mapping.Signature;
18
19public class MethodDefEntry extends MethodEntry {
20
21 private final AccessFlags access;
22 private final Signature signature;
23
24 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) {
25 super(classEntry, name, descriptor);
26 Preconditions.checkNotNull(access, "Method access cannot be null");
27 Preconditions.checkNotNull(signature, "Method signature cannot be null");
28 this.access = access;
29 this.signature = signature;
30 }
31
32 public AccessFlags getAccess() {
33 return access;
34 }
35
36 public Signature getSignature() {
37 return signature;
38 }
39
40 public int getVariableOffset(ClassDefEntry ownerEntry) {
41 // Enum constructors have an implicit "name" and "ordinal" parameter as well as "this"
42 if (ownerEntry.getAccess().isEnum() && getName().startsWith("<")) {
43 return 3;
44 } else {
45 // If we're not static, "this" is bound to index 0
46 return getAccess().isStatic() ? 0 : 1;
47 }
48 }
49
50 @Override
51 public MethodDefEntry updateOwnership(ClassEntry classEntry) {
52 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, signature, access);
53 }
54}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
new file mode 100644
index 00000000..1abc5b12
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
@@ -0,0 +1,80 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.MethodDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class MethodEntry implements Entry {
19
20 protected final ClassEntry classEntry;
21 protected final String name;
22 protected final MethodDescriptor descriptor;
23
24 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
25 Preconditions.checkNotNull(classEntry, "Class cannot be null");
26 Preconditions.checkNotNull(name, "Method name cannot be null");
27 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
28
29 this.classEntry = classEntry;
30 this.name = name;
31 this.descriptor = descriptor;
32 }
33
34 @Override
35 public ClassEntry getOwnerClassEntry() {
36 return this.classEntry;
37 }
38
39 @Override
40 public String getName() {
41 return this.name;
42 }
43
44 public MethodDescriptor getDesc() {
45 return this.descriptor;
46 }
47
48 public boolean isConstructor() {
49 return name.equals("<init>") || name.equals("<clinit>");
50 }
51
52 @Override
53 public String getClassName() {
54 return this.classEntry.getName();
55 }
56
57 @Override
58 public MethodEntry updateOwnership(ClassEntry classEntry) {
59 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
60 }
61
62 @Override
63 public int hashCode() {
64 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
65 }
66
67 @Override
68 public boolean equals(Object other) {
69 return other instanceof MethodEntry && equals((MethodEntry) other);
70 }
71
72 public boolean equals(MethodEntry other) {
73 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
74 }
75
76 @Override
77 public String toString() {
78 return this.classEntry.getName() + "." + this.name + this.descriptor;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
new file mode 100644
index 00000000..73770c53
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
@@ -0,0 +1,48 @@
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.entry;
13
14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MemberReference;
16import com.strobel.assembler.metadata.MethodDefinition;
17import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.MethodDescriptor;
19import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.TypeDescriptor;
21
22public class ProcyonEntryFactory {
23 private final ReferencedEntryPool entryPool;
24
25 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
26 this.entryPool = entryPool;
27 }
28
29 public FieldEntry getFieldEntry(MemberReference def) {
30 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
31 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature());
32 }
33
34 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
35 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
36 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
37 }
38
39 public MethodEntry getMethodEntry(MemberReference def) {
40 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
41 return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature());
42 }
43
44 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
45 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
46 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
47 }
48}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
new file mode 100644
index 00000000..338d209e
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
@@ -0,0 +1,53 @@
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.entry;
13
14import cuchaz.enigma.mapping.MethodDescriptor;
15import cuchaz.enigma.mapping.TypeDescriptor;
16
17import java.util.HashMap;
18import java.util.Map;
19
20public class ReferencedEntryPool {
21 private final Map<String, ClassEntry> classEntries = new HashMap<>();
22 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
23 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
24
25 public ClassEntry getClass(String name) {
26 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(name));
27 }
28
29 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
30 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
31 }
32
33 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
34 String key = name + desc.toString();
35 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
36 }
37
38 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
39 return getField(ownerEntry, name, new TypeDescriptor(desc));
40 }
41
42 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
43 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
44 }
45
46 private Map<String, MethodEntry> getClassMethods(String name) {
47 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
48 }
49
50 private Map<String, FieldEntry> getClassFields(String name) {
51 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
52 }
53}
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
index cc5f650a..b7e6d426 100644
--- a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
+++ b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java
@@ -12,6 +12,7 @@
12package cuchaz.enigma.throwables; 12package cuchaz.enigma.throwables;
13 13
14import java.io.File; 14import java.io.File;
15import java.util.function.Supplier;
15 16
16public class MappingParseException extends Exception { 17public class MappingParseException extends Exception {
17 18
@@ -25,6 +26,12 @@ public class MappingParseException extends Exception {
25 filePath = file.getAbsolutePath(); 26 filePath = file.getAbsolutePath();
26 } 27 }
27 28
29 public MappingParseException(Supplier<String> filenameProvider, int line, String message) {
30 this.line = line;
31 this.message = message;
32 filePath = filenameProvider.get();
33 }
34
28 @Override 35 @Override
29 public String getMessage() { 36 public String getMessage() {
30 return "Line " + line + ": " + message + " in file " + filePath; 37 return "Line " + line + ": " + message + " in file " + filePath;
diff --git a/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java
new file mode 100644
index 00000000..6005b7f7
--- /dev/null
+++ b/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java
@@ -0,0 +1,414 @@
1/*
2 * Originally:
3 * EnumSwitchRewriterTransform.java
4 *
5 * Copyright (c) 2013 Mike Strobel
6 *
7 * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
8 * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
9 *
10 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
11 * A copy of the license can be found in the License.html file at the root of this distribution.
12 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
13 * Apache License, Version 2.0.
14 *
15 * You must not remove this notice, or any other, from this software.
16 */
17
18package oml.ast.transformers;
19
20import com.strobel.assembler.metadata.BuiltinTypes;
21import com.strobel.assembler.metadata.FieldDefinition;
22import com.strobel.assembler.metadata.MethodDefinition;
23import com.strobel.assembler.metadata.TypeDefinition;
24import com.strobel.assembler.metadata.TypeReference;
25import com.strobel.core.SafeCloseable;
26import com.strobel.core.VerifyArgument;
27import com.strobel.decompiler.DecompilerContext;
28import com.strobel.decompiler.languages.java.ast.AssignmentExpression;
29import com.strobel.decompiler.languages.java.ast.AstBuilder;
30import com.strobel.decompiler.languages.java.ast.AstNode;
31import com.strobel.decompiler.languages.java.ast.CaseLabel;
32import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
33import com.strobel.decompiler.languages.java.ast.Expression;
34import com.strobel.decompiler.languages.java.ast.IdentifierExpression;
35import com.strobel.decompiler.languages.java.ast.IndexerExpression;
36import com.strobel.decompiler.languages.java.ast.InvocationExpression;
37import com.strobel.decompiler.languages.java.ast.Keys;
38import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
39import com.strobel.decompiler.languages.java.ast.PrimitiveExpression;
40import com.strobel.decompiler.languages.java.ast.SwitchSection;
41import com.strobel.decompiler.languages.java.ast.SwitchStatement;
42import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
43import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression;
44import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
45
46import java.util.ArrayList;
47import java.util.IdentityHashMap;
48import java.util.LinkedHashMap;
49import java.util.List;
50import java.util.Map;
51
52/**
53 * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to:
54 * - Not rely on a field containing "$SwitchMap$" (Proguard strips it)
55 * - Ignore classes *with* SwitchMap$ names (so the original can handle it)
56 * - Ignores inner synthetics that are not package private
57 */
58@SuppressWarnings("Duplicates")
59public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform {
60 private final DecompilerContext _context;
61
62 public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) {
63 _context = VerifyArgument.notNull(context, "context");
64 }
65
66 @Override
67 public void run(final AstNode compilationUnit) {
68 compilationUnit.acceptVisitor(new Visitor(_context), null);
69 }
70
71 private final static class Visitor extends ContextTrackingVisitor<Void> {
72 private final static class SwitchMapInfo {
73 final String enclosingType;
74 final Map<String, List<SwitchStatement>> switches = new LinkedHashMap<>();
75 final Map<String, Map<Integer, Expression>> mappings = new LinkedHashMap<>();
76
77 TypeDeclaration enclosingTypeDeclaration;
78
79 SwitchMapInfo(final String enclosingType) {
80 this.enclosingType = enclosingType;
81 }
82 }
83
84 private final Map<String, SwitchMapInfo> _switchMaps = new LinkedHashMap<>();
85 private boolean _isSwitchMapWrapper;
86
87 protected Visitor(final DecompilerContext context) {
88 super(context);
89 }
90
91 @Override
92 public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) {
93 final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper;
94 final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION);
95 final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition);
96
97 if (isSwitchMapWrapper) {
98 final String internalName = typeDefinition.getInternalName();
99
100 SwitchMapInfo info = _switchMaps.get(internalName);
101
102 if (info == null) {
103 _switchMaps.put(internalName, info = new SwitchMapInfo(internalName));
104 }
105
106 info.enclosingTypeDeclaration = typeDeclaration;
107 }
108
109 _isSwitchMapWrapper = isSwitchMapWrapper;
110
111 try {
112 super.visitTypeDeclaration(typeDeclaration, p);
113 }
114 finally {
115 _isSwitchMapWrapper = oldIsSwitchMapWrapper;
116 }
117
118 rewrite();
119
120 return null;
121 }
122
123 @Override
124 public Void visitSwitchStatement(final SwitchStatement node, final Void data) {
125 final Expression test = node.getExpression();
126
127 if (test instanceof IndexerExpression) {
128 final IndexerExpression indexer = (IndexerExpression) test;
129 final Expression array = indexer.getTarget();
130 final Expression argument = indexer.getArgument();
131
132 if (!(array instanceof MemberReferenceExpression)) {
133 return super.visitSwitchStatement(node, data);
134 }
135
136 final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array;
137 final Expression arrayOwner = arrayAccess.getTarget();
138 final String mapName = arrayAccess.getMemberName();
139
140 if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) {
141 return super.visitSwitchStatement(node, data);
142 }
143
144 final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner;
145 final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE);
146
147 if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) {
148 return super.visitSwitchStatement(node, data);
149 }
150
151 final InvocationExpression invocation = (InvocationExpression) argument;
152 final Expression invocationTarget = invocation.getTarget();
153
154 if (!(invocationTarget instanceof MemberReferenceExpression)) {
155 return super.visitSwitchStatement(node, data);
156 }
157
158 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
159
160 if (!"ordinal".equals(memberReference.getMemberName())) {
161 return super.visitSwitchStatement(node, data);
162 }
163
164 final String enclosingTypeName = enclosingType.getInternalName();
165
166 SwitchMapInfo info = _switchMaps.get(enclosingTypeName);
167
168 if (info == null) {
169 _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName));
170
171 final TypeDefinition resolvedType = enclosingType.resolve();
172
173 if (resolvedType != null) {
174 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
175
176 if (astBuilder == null) {
177 astBuilder = new AstBuilder(context);
178 }
179
180 try (final SafeCloseable importSuppression = astBuilder.suppressImports()) {
181 final TypeDeclaration declaration = astBuilder.createType(resolvedType);
182
183 declaration.acceptVisitor(this, data);
184 }
185 }
186 }
187
188 List<SwitchStatement> switches = info.switches.get(mapName);
189
190 if (switches == null) {
191 info.switches.put(mapName, switches = new ArrayList<>());
192 }
193
194 switches.add(node);
195 }
196
197 return super.visitSwitchStatement(node, data);
198 }
199
200 @Override
201 public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) {
202 final TypeDefinition currentType = context.getCurrentType();
203 final MethodDefinition currentMethod = context.getCurrentMethod();
204
205 if (_isSwitchMapWrapper &&
206 currentType != null &&
207 currentMethod != null &&
208 currentMethod.isTypeInitializer()) {
209
210 final Expression left = node.getLeft();
211 final Expression right = node.getRight();
212
213 if (left instanceof IndexerExpression &&
214 right instanceof PrimitiveExpression) {
215
216 String mapName = null;
217
218 final Expression array = ((IndexerExpression) left).getTarget();
219 final Expression argument = ((IndexerExpression) left).getArgument();
220
221 if (array instanceof MemberReferenceExpression) {
222 mapName = ((MemberReferenceExpression) array).getMemberName();
223 }
224 else if (array instanceof IdentifierExpression) {
225 mapName = ((IdentifierExpression) array).getIdentifier();
226 }
227
228 if (mapName == null || mapName.startsWith("$SwitchMap$")) {
229 return super.visitAssignmentExpression(node, data);
230 }
231
232 if (!(argument instanceof InvocationExpression)) {
233 return super.visitAssignmentExpression(node, data);
234 }
235
236 final InvocationExpression invocation = (InvocationExpression) argument;
237 final Expression invocationTarget = invocation.getTarget();
238
239 if (!(invocationTarget instanceof MemberReferenceExpression)) {
240 return super.visitAssignmentExpression(node, data);
241 }
242
243 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
244 final Expression memberTarget = memberReference.getTarget();
245
246 if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) {
247 return super.visitAssignmentExpression(node, data);
248 }
249
250 final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget;
251 final Expression outerMemberTarget = outerMemberReference.getTarget();
252
253 if (!(outerMemberTarget instanceof TypeReferenceExpression)) {
254 return super.visitAssignmentExpression(node, data);
255 }
256
257 final String enclosingType = currentType.getInternalName();
258
259 SwitchMapInfo info = _switchMaps.get(enclosingType);
260
261 if (info == null) {
262 _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType));
263
264 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
265
266 if (astBuilder == null) {
267 astBuilder = new AstBuilder(context);
268 }
269
270 info.enclosingTypeDeclaration = astBuilder.createType(currentType);
271 }
272
273 final PrimitiveExpression value = (PrimitiveExpression) right;
274
275 assert value.getValue() instanceof Integer;
276
277 Map<Integer, Expression> mapping = info.mappings.get(mapName);
278
279 if (mapping == null) {
280 info.mappings.put(mapName, mapping = new LinkedHashMap<>());
281 }
282
283 final IdentifierExpression enumValue = new IdentifierExpression( Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName());
284
285 enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE));
286
287 mapping.put(((Number) value.getValue()).intValue(), enumValue);
288 }
289 }
290
291 return super.visitAssignmentExpression(node, data);
292 }
293
294 private void rewrite() {
295 if (_switchMaps.isEmpty()) {
296 return;
297 }
298
299 for (final SwitchMapInfo info : _switchMaps.values()) {
300 rewrite(info);
301 }
302
303 //
304 // Remove switch map type wrappers that are no longer referenced.
305 //
306
307 outer:
308 for (final SwitchMapInfo info : _switchMaps.values()) {
309 for (final String mapName : info.switches.keySet()) {
310 final List<SwitchStatement> switches = info.switches.get(mapName);
311
312 if (switches != null && !switches.isEmpty()) {
313 continue outer;
314 }
315 }
316
317 final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration;
318
319 if (enclosingTypeDeclaration != null) {
320 enclosingTypeDeclaration.remove();
321 }
322 }
323 }
324
325 private void rewrite(final SwitchMapInfo info) {
326 if (info.switches.isEmpty()) {
327 return;
328 }
329
330 for (final String mapName : info.switches.keySet()) {
331 final List<SwitchStatement> switches = info.switches.get(mapName);
332 final Map<Integer, Expression> mappings = info.mappings.get(mapName);
333
334 if (switches != null && mappings != null) {
335 for (int i = 0; i < switches.size(); i++) {
336 if (rewriteSwitch(switches.get(i), mappings)) {
337 switches.remove(i--);
338 }
339 }
340 }
341 }
342 }
343
344 private boolean rewriteSwitch(final SwitchStatement s, final Map<Integer, Expression> mappings) {
345 final Map<Expression, Expression> replacements = new IdentityHashMap<>();
346
347 for (final SwitchSection section : s.getSwitchSections()) {
348 for (final CaseLabel caseLabel : section.getCaseLabels()) {
349 final Expression expression = caseLabel.getExpression();
350
351 if (expression.isNull()) {
352 continue;
353 }
354
355 if (expression instanceof PrimitiveExpression) {
356 final Object value = ((PrimitiveExpression) expression).getValue();
357
358 if (value instanceof Integer) {
359 final Expression replacement = mappings.get(value);
360
361 if (replacement != null) {
362 replacements.put(expression, replacement);
363 continue;
364 }
365 }
366 }
367
368 //
369 // If we can't rewrite all cases, we abort.
370 //
371
372 return false;
373 }
374 }
375
376 final IndexerExpression indexer = (IndexerExpression) s.getExpression();
377 final InvocationExpression argument = (InvocationExpression) indexer.getArgument();
378 final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget();
379 final Expression newTest = memberReference.getTarget();
380
381 newTest.remove();
382 indexer.replaceWith(newTest);
383
384 for (final Map.Entry<Expression, Expression> entry : replacements.entrySet()) {
385 entry.getKey().replaceWith(entry.getValue().clone());
386 }
387
388 return true;
389 }
390
391 private static boolean isSwitchMapWrapper(final TypeReference type) {
392 if (type == null) {
393 return false;
394 }
395
396 final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type
397 : type.resolve();
398
399 if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) {
400 return false;
401 }
402
403 for (final FieldDefinition field : definition.getDeclaredFields()) {
404 if (!field.getName().startsWith("$SwitchMap$") &&
405 BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) {
406
407 return true;
408 }
409 }
410
411 return false;
412 }
413 }
414} \ No newline at end of file
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java
index e6c1b746..9babf1e3 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.entry.ReferencedEntryPool;
15import org.junit.BeforeClass; 17import org.junit.BeforeClass;
16import org.junit.Test; 18import org.junit.Test;
17 19
@@ -23,14 +25,14 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
23 25
24public class TestDeobfed { 26public class TestDeobfed {
25 27
26 private static JarFile jar; 28 private static ParsedJar jar;
27 private static JarIndex index; 29 private static JarIndex index;
28 30
29 @BeforeClass 31 @BeforeClass
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 ParsedJar(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(jar, true);
35 } 37 }
36 38
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
index 62a52861..63a6f552 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
@@ -12,7 +12,7 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.ClassEntry; 15import cuchaz.enigma.mapping.entry.ClassEntry;
16import org.junit.Test; 16import org.junit.Test;
17 17
18import java.io.IOException; 18import java.io.IOException;
diff --git a/src/test/java/cuchaz/enigma/TestEntryFactory.java b/src/test/java/cuchaz/enigma/TestEntryFactory.java
index 1c527f53..4f52609e 100644
--- a/src/test/java/cuchaz/enigma/TestEntryFactory.java
+++ b/src/test/java/cuchaz/enigma/TestEntryFactory.java
@@ -13,6 +13,9 @@ package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.FieldEntry;
18import cuchaz.enigma.mapping.entry.MethodEntry;
16 19
17public class TestEntryFactory { 20public class TestEntryFactory {
18 21
@@ -25,7 +28,7 @@ public class TestEntryFactory {
25 } 28 }
26 29
27 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { 30 public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) {
28 return new FieldEntry(classEntry, fieldName, new Type(fieldType)); 31 return new FieldEntry(classEntry, fieldName, new TypeDescriptor(fieldType));
29 } 32 }
30 33
31 public static MethodEntry newMethod(String className, String methodName, String methodSignature) { 34 public static MethodEntry newMethod(String className, String methodName, String methodSignature) {
@@ -33,30 +36,14 @@ public class TestEntryFactory {
33 } 36 }
34 37
35 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { 38 public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) {
36 return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); 39 return new MethodEntry(classEntry, methodName, new MethodDescriptor(methodSignature));
37 } 40 }
38 41
39 public static ConstructorEntry newConstructor(String className, String signature) { 42 public static EntryReference<FieldEntry, MethodEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) {
40 return newConstructor(newClass(className), signature); 43 return new EntryReference<>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature));
41 } 44 }
42 45
43 public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { 46 public static EntryReference<MethodEntry, MethodEntry> newBehaviorReferenceByMethod(MethodEntry methodEntry, String callerClassName, String callerName, String callerSignature) {
44 return new ConstructorEntry(classEntry, new Signature(signature)); 47 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 } 48 }
62} 49}
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 38db0df9..843a63c7 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.mapping.ClassEntry; 15import cuchaz.enigma.analysis.ParsedJar;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.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,8 +41,8 @@ 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 ParsedJar jar = new ParsedJar(new JarFile("build/test-obf/innerClasses.jar"));
50 index.indexJar(jar, true); 46 index.indexJar(jar, true);
51 deobfuscator = new Deobfuscator(jar); 47 deobfuscator = new Deobfuscator(jar);
52 } 48 }
@@ -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..763639a4 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.entry.ClassEntry;
18import cuchaz.enigma.mapping.entry.MethodDefEntry;
19import cuchaz.enigma.mapping.entry.MethodEntry;
20import cuchaz.enigma.mapping.entry.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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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..23df1a99 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.entry.*;
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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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.getMethodsReferencing(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..b4529ddc 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -13,6 +13,7 @@ package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.*; 14import cuchaz.enigma.analysis.*;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.mapping.*;
16import cuchaz.enigma.mapping.entry.*;
16import org.junit.Test; 17import org.junit.Test;
17 18
18import java.util.Collection; 19import java.util.Collection;
@@ -28,16 +29,16 @@ public class TestJarIndexLoneClass {
28 private JarIndex index; 29 private JarIndex index;
29 30
30 public TestJarIndexLoneClass() 31 public TestJarIndexLoneClass()
31 throws Exception { 32 throws Exception {
32 index = new JarIndex(); 33 index = new JarIndex(new ReferencedEntryPool());
33 index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false); 34 index.indexJar(new ParsedJar(new JarFile("build/test-obf/loneClass.jar")), false);
34 } 35 }
35 36
36 @Test 37 @Test
37 public void obfEntries() { 38 public void obfEntries() {
38 assertThat(index.getObfClassEntries(), containsInAnyOrder( 39 assertThat(index.getObfClassEntries(), containsInAnyOrder(
39 newClass("cuchaz/enigma/inputs/Keep"), 40 newClass("cuchaz/enigma/inputs/Keep"),
40 newClass("a") 41 newClass("a")
41 )); 42 ));
42 } 43 }
43 44
@@ -61,7 +62,7 @@ public class TestJarIndexLoneClass {
61 62
62 @Test 63 @Test
63 public void classInheritance() { 64 public void classInheritance() {
64 ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a")); 65 ClassInheritanceTreeNode node = index.getClassInheritance(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a"));
65 assertThat(node, is(not(nullValue()))); 66 assertThat(node, is(not(nullValue())));
66 assertThat(node.getObfClassName(), is("a")); 67 assertThat(node.getObfClassName(), is("a"));
67 assertThat(node.getChildCount(), is(0)); 68 assertThat(node.getChildCount(), is(0));
@@ -70,7 +71,7 @@ public class TestJarIndexLoneClass {
70 @Test 71 @Test
71 public void methodInheritance() { 72 public void methodInheritance() {
72 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 73 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
73 MethodInheritanceTreeNode node = index.getMethodInheritance(new Translator(), source); 74 MethodInheritanceTreeNode node = index.getMethodInheritance(new DirectionalTranslator(new ReferencedEntryPool()), source);
74 assertThat(node, is(not(nullValue()))); 75 assertThat(node, is(not(nullValue())));
75 assertThat(node.getMethodEntry(), is(source)); 76 assertThat(node.getMethodEntry(), is(source));
76 assertThat(node.getChildCount(), is(0)); 77 assertThat(node.getChildCount(), is(0));
@@ -78,21 +79,21 @@ public class TestJarIndexLoneClass {
78 79
79 @Test 80 @Test
80 public void classImplementations() { 81 public void classImplementations() {
81 ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a")); 82 ClassImplementationsTreeNode node = index.getClassImplementations(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a"));
82 assertThat(node, is(nullValue())); 83 assertThat(node, is(nullValue()));
83 } 84 }
84 85
85 @Test 86 @Test
86 public void methodImplementations() { 87 public void methodImplementations() {
87 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 88 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
88 assertThat(index.getMethodImplementations(new Translator(), source), is(empty())); 89 assertThat(index.getMethodImplementations(new DirectionalTranslator(new ReferencedEntryPool()), source), is(empty()));
89 } 90 }
90 91
91 @Test 92 @Test
92 public void relatedMethodImplementations() { 93 public void relatedMethodImplementations() {
93 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); 94 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;"));
94 assertThat(entries, containsInAnyOrder( 95 assertThat(entries, containsInAnyOrder(
95 newMethod("a", "a", "()Ljava/lang/String;") 96 newMethod("a", "a", "()Ljava/lang/String;")
96 )); 97 ));
97 } 98 }
98 99
@@ -100,16 +101,16 @@ public class TestJarIndexLoneClass {
100 @SuppressWarnings("unchecked") 101 @SuppressWarnings("unchecked")
101 public void fieldReferences() { 102 public void fieldReferences() {
102 FieldEntry source = newField("a", "a", "Ljava/lang/String;"); 103 FieldEntry source = newField("a", "a", "Ljava/lang/String;");
103 Collection<EntryReference<FieldEntry, BehaviorEntry>> references = index.getFieldReferences(source); 104 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getFieldReferences(source);
104 assertThat(references, containsInAnyOrder( 105 assertThat(references, containsInAnyOrder(
105 newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"), 106 newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"),
106 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") 107 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")
107 )); 108 ));
108 } 109 }
109 110
110 @Test 111 @Test
111 public void behaviorReferences() { 112 public void behaviorReferences() {
112 assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); 113 assertThat(index.getMethodsReferencing(newMethod("a", "a", "()Ljava/lang/String;")), is(empty()));
113 } 114 }
114 115
115 @Test 116 @Test
@@ -123,11 +124,6 @@ public class TestJarIndexLoneClass {
123 } 124 }
124 125
125 @Test 126 @Test
126 public void isAnonymousClass() {
127 assertThat(index.isAnonymousClass(newClass("a")), is(false));
128 }
129
130 @Test
131 public void interfaces() { 127 public void interfaces() {
132 assertThat(index.getInterfaces("a"), is(empty())); 128 assertThat(index.getInterfaces("a"), is(empty()));
133 } 129 }
@@ -149,7 +145,7 @@ public class TestJarIndexLoneClass {
149 assertThat(index.containsObfField(newField("a", "a", "Ljava/lang/String;")), is(true)); 145 assertThat(index.containsObfField(newField("a", "a", "Ljava/lang/String;")), is(true));
150 assertThat(index.containsObfField(newField("a", "b", "Ljava/lang/String;")), is(false)); 146 assertThat(index.containsObfField(newField("a", "b", "Ljava/lang/String;")), is(false));
151 assertThat(index.containsObfField(newField("a", "a", "LFoo;")), is(false)); 147 assertThat(index.containsObfField(newField("a", "a", "LFoo;")), is(false));
152 assertThat(index.containsObfBehavior(newMethod("a", "a", "()Ljava/lang/String;")), is(true)); 148 assertThat(index.containsObfMethod(newMethod("a", "a", "()Ljava/lang/String;")), is(true));
153 assertThat(index.containsObfBehavior(newMethod("a", "b", "()Ljava/lang/String;")), is(false)); 149 assertThat(index.containsObfMethod(newMethod("a", "b", "()Ljava/lang/String;")), is(false));
154 } 150 }
155} 151}
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/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java
index 6e9e5aec..07542753 100644
--- a/src/test/java/cuchaz/enigma/TestSourceIndex.java
+++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java
@@ -13,7 +13,7 @@ package cuchaz.enigma;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import com.strobel.decompiler.languages.java.ast.CompilationUnit; 15import com.strobel.decompiler.languages.java.ast.CompilationUnit;
16import cuchaz.enigma.mapping.ClassEntry; 16import cuchaz.enigma.mapping.entry.ClassEntry;
17import org.junit.Test; 17import org.junit.Test;
18 18
19import java.io.File; 19import java.io.File;
diff --git a/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
index e40d5fdc..0e98da7f 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.entry.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/TestTranslator.java b/src/test/java/cuchaz/enigma/TestTranslator.java
index b63dff86..9b6eb916 100644
--- a/src/test/java/cuchaz/enigma/TestTranslator.java
+++ b/src/test/java/cuchaz/enigma/TestTranslator.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.Entry; 14import cuchaz.enigma.mapping.entry.Entry;
15import cuchaz.enigma.mapping.Mappings; 15import cuchaz.enigma.mapping.Mappings;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.mapping.Translator;
17import org.junit.BeforeClass; 17import org.junit.BeforeClass;
diff --git a/src/test/java/cuchaz/enigma/TestType.java b/src/test/java/cuchaz/enigma/TestType.java
deleted file mode 100644
index 43dacb0c..00000000
--- a/src/test/java/cuchaz/enigma/TestType.java
+++ /dev/null
@@ -1,243 +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.Type;
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 TestType {
23
24 @Test
25 public void isVoid() {
26 assertThat(new Type("V").isVoid(), is(true));
27 assertThat(new Type("Z").isVoid(), is(false));
28 assertThat(new Type("B").isVoid(), is(false));
29 assertThat(new Type("C").isVoid(), is(false));
30 assertThat(new Type("I").isVoid(), is(false));
31 assertThat(new Type("J").isVoid(), is(false));
32 assertThat(new Type("F").isVoid(), is(false));
33 assertThat(new Type("D").isVoid(), is(false));
34 assertThat(new Type("LFoo;").isVoid(), is(false));
35 assertThat(new Type("[I").isVoid(), is(false));
36 }
37
38 @Test
39 public void isPrimitive() {
40 assertThat(new Type("V").isPrimitive(), is(false));
41 assertThat(new Type("Z").isPrimitive(), is(true));
42 assertThat(new Type("B").isPrimitive(), is(true));
43 assertThat(new Type("C").isPrimitive(), is(true));
44 assertThat(new Type("I").isPrimitive(), is(true));
45 assertThat(new Type("J").isPrimitive(), is(true));
46 assertThat(new Type("F").isPrimitive(), is(true));
47 assertThat(new Type("D").isPrimitive(), is(true));
48 assertThat(new Type("LFoo;").isPrimitive(), is(false));
49 assertThat(new Type("[I").isPrimitive(), is(false));
50 }
51
52 @Test
53 public void getPrimitive() {
54 assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean));
55 assertThat(new Type("B").getPrimitive(), is(Type.Primitive.Byte));
56 assertThat(new Type("C").getPrimitive(), is(Type.Primitive.Character));
57 assertThat(new Type("I").getPrimitive(), is(Type.Primitive.Integer));
58 assertThat(new Type("J").getPrimitive(), is(Type.Primitive.Long));
59 assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float));
60 assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double));
61 }
62
63 @Test
64 public void isClass() {
65 assertThat(new Type("V").isClass(), is(false));
66 assertThat(new Type("Z").isClass(), is(false));
67 assertThat(new Type("B").isClass(), is(false));
68 assertThat(new Type("C").isClass(), is(false));
69 assertThat(new Type("I").isClass(), is(false));
70 assertThat(new Type("J").isClass(), is(false));
71 assertThat(new Type("F").isClass(), is(false));
72 assertThat(new Type("D").isClass(), is(false));
73 assertThat(new Type("LFoo;").isClass(), is(true));
74 assertThat(new Type("[I").isClass(), is(false));
75 }
76
77 @Test
78 public void getClassEntry() {
79 assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo")));
80 assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String")));
81 }
82
83 @Test
84 public void getArrayClassEntry() {
85 assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo")));
86 assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String")));
87 }
88
89 @Test
90 public void isArray() {
91 assertThat(new Type("V").isArray(), is(false));
92 assertThat(new Type("Z").isArray(), is(false));
93 assertThat(new Type("B").isArray(), is(false));
94 assertThat(new Type("C").isArray(), is(false));
95 assertThat(new Type("I").isArray(), is(false));
96 assertThat(new Type("J").isArray(), is(false));
97 assertThat(new Type("F").isArray(), is(false));
98 assertThat(new Type("D").isArray(), is(false));
99 assertThat(new Type("LFoo;").isArray(), is(false));
100 assertThat(new Type("[I").isArray(), is(true));
101 }
102
103 @Test
104 public void getArrayDimension() {
105 assertThat(new Type("[I").getArrayDimension(), is(1));
106 assertThat(new Type("[[I").getArrayDimension(), is(2));
107 assertThat(new Type("[[[I").getArrayDimension(), is(3));
108 }
109
110 @Test
111 public void getArrayType() {
112 assertThat(new Type("[I").getArrayType(), is(new Type("I")));
113 assertThat(new Type("[[I").getArrayType(), is(new Type("I")));
114 assertThat(new Type("[[[I").getArrayType(), is(new Type("I")));
115 assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;")));
116 }
117
118 @Test
119 public void hasClass() {
120 assertThat(new Type("LFoo;").hasClass(), is(true));
121 assertThat(new Type("Ljava/lang/String;").hasClass(), is(true));
122 assertThat(new Type("[LBar;").hasClass(), is(true));
123 assertThat(new Type("[[[LCat;").hasClass(), is(true));
124
125 assertThat(new Type("V").hasClass(), is(false));
126 assertThat(new Type("[I").hasClass(), is(false));
127 assertThat(new Type("[[[I").hasClass(), is(false));
128 assertThat(new Type("Z").hasClass(), is(false));
129 }
130
131 @Test
132 public void parseVoid() {
133 final String answer = "V";
134 assertThat(Type.parseFirst("V"), is(answer));
135 assertThat(Type.parseFirst("VVV"), is(answer));
136 assertThat(Type.parseFirst("VIJ"), is(answer));
137 assertThat(Type.parseFirst("V[I"), is(answer));
138 assertThat(Type.parseFirst("VLFoo;"), is(answer));
139 assertThat(Type.parseFirst("V[LFoo;"), is(answer));
140 }
141
142 @Test
143 public void parsePrimitive() {
144 final String answer = "I";
145 assertThat(Type.parseFirst("I"), is(answer));
146 assertThat(Type.parseFirst("III"), is(answer));
147 assertThat(Type.parseFirst("IJZ"), is(answer));
148 assertThat(Type.parseFirst("I[I"), is(answer));
149 assertThat(Type.parseFirst("ILFoo;"), is(answer));
150 assertThat(Type.parseFirst("I[LFoo;"), is(answer));
151 }
152
153 @Test
154 public void parseClass() {
155 {
156 final String answer = "LFoo;";
157 assertThat(Type.parseFirst("LFoo;"), is(answer));
158 assertThat(Type.parseFirst("LFoo;I"), is(answer));
159 assertThat(Type.parseFirst("LFoo;JZ"), is(answer));
160 assertThat(Type.parseFirst("LFoo;[I"), is(answer));
161 assertThat(Type.parseFirst("LFoo;LFoo;"), is(answer));
162 assertThat(Type.parseFirst("LFoo;[LFoo;"), is(answer));
163 }
164 {
165 final String answer = "Ljava/lang/String;";
166 assertThat(Type.parseFirst("Ljava/lang/String;"), is(answer));
167 assertThat(Type.parseFirst("Ljava/lang/String;I"), is(answer));
168 assertThat(Type.parseFirst("Ljava/lang/String;JZ"), is(answer));
169 assertThat(Type.parseFirst("Ljava/lang/String;[I"), is(answer));
170 assertThat(Type.parseFirst("Ljava/lang/String;LFoo;"), is(answer));
171 assertThat(Type.parseFirst("Ljava/lang/String;[LFoo;"), is(answer));
172 }
173 }
174
175 @Test
176 public void parseArray() {
177 {
178 final String answer = "[I";
179 assertThat(Type.parseFirst("[I"), is(answer));
180 assertThat(Type.parseFirst("[III"), is(answer));
181 assertThat(Type.parseFirst("[IJZ"), is(answer));
182 assertThat(Type.parseFirst("[I[I"), is(answer));
183 assertThat(Type.parseFirst("[ILFoo;"), is(answer));
184 }
185 {
186 final String answer = "[[I";
187 assertThat(Type.parseFirst("[[I"), is(answer));
188 assertThat(Type.parseFirst("[[III"), is(answer));
189 assertThat(Type.parseFirst("[[IJZ"), is(answer));
190 assertThat(Type.parseFirst("[[I[I"), is(answer));
191 assertThat(Type.parseFirst("[[ILFoo;"), is(answer));
192 }
193 {
194 final String answer = "[LFoo;";
195 assertThat(Type.parseFirst("[LFoo;"), is(answer));
196 assertThat(Type.parseFirst("[LFoo;II"), is(answer));
197 assertThat(Type.parseFirst("[LFoo;JZ"), is(answer));
198 assertThat(Type.parseFirst("[LFoo;[I"), is(answer));
199 assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer));
200 }
201 }
202
203 @Test
204 public void equals() {
205 assertThat(new Type("V"), is(new Type("V")));
206 assertThat(new Type("Z"), is(new Type("Z")));
207 assertThat(new Type("B"), is(new Type("B")));
208 assertThat(new Type("C"), is(new Type("C")));
209 assertThat(new Type("I"), is(new Type("I")));
210 assertThat(new Type("J"), is(new Type("J")));
211 assertThat(new Type("F"), is(new Type("F")));
212 assertThat(new Type("D"), is(new Type("D")));
213 assertThat(new Type("LFoo;"), is(new Type("LFoo;")));
214 assertThat(new Type("[I"), is(new Type("[I")));
215 assertThat(new Type("[[[I"), is(new Type("[[[I")));
216 assertThat(new Type("[LFoo;"), is(new Type("[LFoo;")));
217
218 assertThat(new Type("V"), is(not(new Type("I"))));
219 assertThat(new Type("I"), is(not(new Type("J"))));
220 assertThat(new Type("I"), is(not(new Type("LBar;"))));
221 assertThat(new Type("I"), is(not(new Type("[I"))));
222 assertThat(new Type("LFoo;"), is(not(new Type("LBar;"))));
223 assertThat(new Type("[I"), is(not(new Type("[Z"))));
224 assertThat(new Type("[[[I"), is(not(new Type("[I"))));
225 assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;"))));
226 }
227
228 @Test
229 public void testToString() {
230 assertThat(new Type("V").toString(), is("V"));
231 assertThat(new Type("Z").toString(), is("Z"));
232 assertThat(new Type("B").toString(), is("B"));
233 assertThat(new Type("C").toString(), is("C"));
234 assertThat(new Type("I").toString(), is("I"));
235 assertThat(new Type("J").toString(), is("J"));
236 assertThat(new Type("F").toString(), is("F"));
237 assertThat(new Type("D").toString(), is("D"));
238 assertThat(new Type("LFoo;").toString(), is("LFoo;"));
239 assertThat(new Type("[I").toString(), is("[I"));
240 assertThat(new Type("[[[I").toString(), is("[[[I"));
241 assertThat(new Type("[LFoo;").toString(), is("[LFoo;"));
242 }
243}
diff --git a/src/test/java/cuchaz/enigma/TestTypeDescriptor.java b/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
new file mode 100644
index 00000000..90fd6351
--- /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;").getTypeEntry(), is(newClass("Foo")));
80 assertThat(new TypeDescriptor("Ljava/lang/String;").getTypeEntry(), is(newClass("java/lang/String")));
81 }
82
83 @Test
84 public void getArrayClassEntry() {
85 assertThat(new TypeDescriptor("[LFoo;").getTypeEntry(), is(newClass("Foo")));
86 assertThat(new TypeDescriptor("[[[Ljava/lang/String;").getTypeEntry(), 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}
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java
index c6ced488..d863a5ae 100644
--- a/src/test/java/cuchaz/enigma/TokenChecker.java
+++ b/src/test/java/cuchaz/enigma/TokenChecker.java
@@ -16,7 +16,7 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit;
16import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
17import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.analysis.Token; 18import cuchaz.enigma.analysis.Token;
19import cuchaz.enigma.mapping.Entry; 19import cuchaz.enigma.mapping.entry.Entry;
20 20
21import java.io.IOException; 21import java.io.IOException;
22import java.util.Collection; 22import java.util.Collection;