summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Gegy2019-01-30 21:05:32 +0200
committerGravatar GitHub2019-01-30 21:05:32 +0200
commitba7a354efae7d49833c887cf147ac940c975a1fa (patch)
tree02e14fda81dd5984e24f2df392c57c6e829fc875 /src
parentRewrite the Jenkinsfile to use the new declarative pipeline syntax, lets hope... (diff)
downloadenigma-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.gz
enigma-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.xz
enigma-ba7a354efae7d49833c887cf147ac940c975a1fa.zip
Remap sources (#106)
* Source remapping beginnings * Fix navigation to remapped classes * Translate identifier info reference * Remap local variables with default names in source * Caching translator * Fix lack of highlighting for first opened class * Fix unicode variable names * Unicode checker shouldn't be checking just alphanumeric * Fix package tree being built from obf names * Don't index `this` as method call for method::reference * Apply proposed names * Fix source export issues * Replace unicode var names at bytecode level uniquely * Drop imports from editor source * Class selector fixes * Delta keep track of base mappings to enable lookup of old names * Optimize source remapping by remapping source with a StringBuffer instead of copying * Bump version
Diffstat (limited to 'src')
-rw-r--r--src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java1
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java4
-rw-r--r--src/main/java/cuchaz/enigma/CompiledSource.java10
-rw-r--r--src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java (renamed from src/main/java/cuchaz/enigma/TranslatingTypeLoader.java)101
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java267
-rw-r--r--src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java19
-rw-r--r--src/main/java/cuchaz/enigma/SourceProvider.java97
-rw-r--r--src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java27
-rw-r--r--src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java33
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java34
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java86
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java14
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java64
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java9
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Token.java6
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java16
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java20
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java18
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java22
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java9
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java80
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java7
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java111
-rw-r--r--src/main/java/cuchaz/enigma/config/Config.java5
-rw-r--r--src/main/java/cuchaz/enigma/config/Themes.java17
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java182
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java94
-rw-r--r--src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java129
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java102
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java305
-rw-r--r--src/main/java/cuchaz/enigma/gui/SourceRemapper.java64
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java8
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java7
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java8
-rw-r--r--src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java44
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java125
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java36
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java14
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java19
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java2
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java6
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java2
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java21
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java22
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java18
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java60
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/Signature.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java5
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java11
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java4
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java7
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfed.java45
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfuscator.java3
-rw-r--r--src/test/java/cuchaz/enigma/TestInnerClasses.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestSourceIndex.java9
-rw-r--r--src/test/java/cuchaz/enigma/TokenChecker.java14
58 files changed, 1264 insertions, 1203 deletions
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
index fe13321c..b2aed84d 100644
--- a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
@@ -16,6 +16,7 @@ public class CachingClasspathTypeLoader extends CachingTypeLoader {
16 16
17 private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); 17 private final ITypeLoader classpathLoader = new ClasspathTypeLoader();
18 18
19 @Override
19 protected byte[] doLoad(String className) { 20 protected byte[] doLoad(String className) {
20 Buffer parentBuf = new Buffer(); 21 Buffer parentBuf = new Buffer();
21 if (classpathLoader.tryLoadType(className, parentBuf)) { 22 if (classpathLoader.tryLoadType(className, parentBuf)) {
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index a84cd5e7..7c0a3d53 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -58,7 +58,7 @@ public class CommandMain {
58 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); 58 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true));
59 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); 59 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false));
60 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 60 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
61 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); 61 deobfuscator.writeSources(fileJarOut.toPath(), new ConsoleProgressListener());
62 } 62 }
63 63
64 private static void deobfuscate(String[] args) throws Exception { 64 private static void deobfuscate(String[] args) throws Exception {
@@ -66,7 +66,7 @@ public class CommandMain {
66 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 66 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
67 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); 67 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false));
68 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 68 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
69 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); 69 deobfuscator.writeTransformedJar(fileJarOut, new ConsoleProgressListener());
70 } 70 }
71 71
72 private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception { 72 private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception {
diff --git a/src/main/java/cuchaz/enigma/CompiledSource.java b/src/main/java/cuchaz/enigma/CompiledSource.java
new file mode 100644
index 00000000..fc051d33
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/CompiledSource.java
@@ -0,0 +1,10 @@
1package cuchaz.enigma;
2
3import org.objectweb.asm.tree.ClassNode;
4
5import javax.annotation.Nullable;
6
7public interface CompiledSource {
8 @Nullable
9 ClassNode getClassNode(String name);
10}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
index 4c1f695b..b1a8cd58 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
@@ -14,11 +14,6 @@ package cuchaz.enigma;
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.strobel.assembler.metadata.Buffer; 15import com.strobel.assembler.metadata.Buffer;
16import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.analysis.ParsedJar;
19import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
20import cuchaz.enigma.translation.Translator;
21import cuchaz.enigma.translation.representation.ReferencedEntryPool;
22import cuchaz.enigma.translation.representation.entry.ClassEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
23import org.objectweb.asm.ClassWriter; 18import org.objectweb.asm.ClassWriter;
24import org.objectweb.asm.Opcodes; 19import org.objectweb.asm.Opcodes;
@@ -27,61 +22,57 @@ import org.objectweb.asm.tree.ClassNode;
27import org.objectweb.asm.tree.MethodInsnNode; 22import org.objectweb.asm.tree.MethodInsnNode;
28import org.objectweb.asm.tree.MethodNode; 23import org.objectweb.asm.tree.MethodNode;
29 24
25import java.util.Collection;
30import java.util.List; 26import java.util.List;
31 27
32public class TranslatingTypeLoader extends CachingTypeLoader implements ITranslatingTypeLoader { 28public class CompiledSourceTypeLoader extends CachingTypeLoader {
33 //Store one instance as the classpath shouldnt change during load 29 //Store one instance as the classpath shouldn't change during load
34 private static final ITypeLoader defaultTypeLoader = new CachingClasspathTypeLoader(); 30 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader();
35 31
36 private final ParsedJar jar; 32 private final CompiledSource compiledSource;
37 private final JarIndex jarIndex; 33
38 private final ReferencedEntryPool entryPool; 34 public CompiledSourceTypeLoader(CompiledSource compiledSource) {
39 private final Translator obfuscatingTranslator; 35 this.compiledSource = compiledSource;
40 private final Translator deobfuscatingTranslator;
41
42 public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) {
43 this.jar = jar;
44 this.jarIndex = jarIndex;
45 this.entryPool = entryPool;
46 this.obfuscatingTranslator = obfuscatingTranslator;
47 this.deobfuscatingTranslator = deobfuscatingTranslator;
48 } 36 }
49 37
38 @Override
50 protected byte[] doLoad(String className) { 39 protected byte[] doLoad(String className) {
51 byte[] data = loadType(className); 40 byte[] data = loadType(className);
52 if (data == null) { 41 if (data == null) {
53 // chain to default desc loader 42 return loadClasspath(className);
54 Buffer parentBuf = new Buffer();
55 if (defaultTypeLoader.tryLoadType(className, parentBuf)) {
56 return parentBuf.array();
57 }
58 return EMPTY_ARRAY;//need to return *something* as null means no store
59 } 43 }
44
60 return data; 45 return data;
61 } 46 }
62 47
63 private byte[] loadType(String className) { 48 private byte[] loadClasspath(String name) {
64 49 Buffer parentBuf = new Buffer();
65 // NOTE: don't know if class name is obf or deobf 50 if (CLASSPATH_TYPE_LOADER.tryLoadType(name, parentBuf)) {
66 ClassEntry classEntry = new ClassEntry(className); 51 return parentBuf.array();
67 ClassEntry obfClassEntry = this.obfuscatingTranslator.translate(classEntry);
68
69 // is this a class we should even know about?
70 if (!jarIndex.getEntryIndex().hasClass(obfClassEntry)) {
71 return null;
72 } 52 }
53 return EMPTY_ARRAY;
54 }
73 55
74 // DEBUG 56 private byte[] loadType(String className) {
75 //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); 57 ClassEntry entry = new ClassEntry(className);
76 58
77 // find the class in the jar 59 // find the class in the jar
78 ClassNode node = findClassInJar(obfClassEntry); 60 ClassNode node = findClassNode(entry);
79 if (node == null) { 61 if (node == null) {
80 // couldn't find it 62 // couldn't find it
81 return null; 63 return null;
82 } 64 }
83 65
66 removeRedundantClassCalls(node);
67
68 ClassWriter writer = new ClassWriter(0);
69 node.accept(writer);
84 70
71 // we have a transformed class!
72 return writer.toByteArray();
73 }
74
75 private void removeRedundantClassCalls(ClassNode node) {
85 // remove <obj>.getClass() calls that are seemingly injected 76 // remove <obj>.getClass() calls that are seemingly injected
86 // DUP 77 // DUP
87 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; 78 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
@@ -105,19 +96,12 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
105 insnNode = insnNode.getNext(); 96 insnNode = insnNode.getNext();
106 } 97 }
107 } 98 }
108
109 ClassWriter writer = new ClassWriter(0);
110 transformInto(node, writer);
111
112 // we have a transformed class!
113 return writer.toByteArray();
114 } 99 }
115 100
116 private ClassNode findClassInJar(ClassEntry obfClassEntry) { 101 private ClassNode findClassNode(ClassEntry entry) {
117
118 // try to find the class in the jar 102 // try to find the class in the jar
119 for (String className : getClassNamesToTry(obfClassEntry)) { 103 for (String className : getClassNamesToTry(entry)) {
120 ClassNode node = this.jar.getClassNode(className); 104 ClassNode node = compiledSource.getClassNode(className);
121 if (node != null) { 105 if (node != null) {
122 return node; 106 return node;
123 } 107 }
@@ -127,28 +111,15 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
127 return null; 111 return null;
128 } 112 }
129 113
130 @Override 114 private Collection<String> getClassNamesToTry(ClassEntry entry) {
131 public List<String> getClassNamesToTry(String className) {
132 return getClassNamesToTry(this.obfuscatingTranslator.translate(new ClassEntry(className)));
133 }
134
135 @Override
136 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
137 List<String> classNamesToTry = Lists.newArrayList(); 115 List<String> classNamesToTry = Lists.newArrayList();
138 classNamesToTry.add(obfClassEntry.getFullName()); 116 classNamesToTry.add(entry.getFullName());
139 117
140 ClassEntry outerClass = obfClassEntry.getOuterClass(); 118 ClassEntry outerClass = entry.getOuterClass();
141 if (outerClass != null) { 119 if (outerClass != null) {
142 classNamesToTry.addAll(getClassNamesToTry(outerClass)); 120 classNamesToTry.addAll(getClassNamesToTry(outerClass));
143 } 121 }
144 122
145 return classNamesToTry; 123 return classNamesToTry;
146 } 124 }
147
148 @Override
149 public String transformInto(ClassNode node, ClassWriter writer) {
150 node.accept(new TranslationClassVisitor(deobfuscatingTranslator, entryPool, Opcodes.ASM5, writer));
151 return deobfuscatingTranslator.translate(new ClassEntry(node.name)).getFullName();
152 }
153
154} 125}
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 076c5468..ef452b0f 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -11,36 +11,39 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.base.Functions;
14import com.google.common.base.Stopwatch; 15import com.google.common.base.Stopwatch;
15import com.google.common.collect.Lists;
16import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
17import com.strobel.assembler.metadata.MetadataSystem; 17import com.strobel.assembler.metadata.MetadataSystem;
18import com.strobel.assembler.metadata.TypeDefinition; 18import com.strobel.assembler.metadata.TypeDefinition;
19import com.strobel.assembler.metadata.TypeReference; 19import com.strobel.assembler.metadata.TypeReference;
20import com.strobel.decompiler.DecompilerContext;
21import com.strobel.decompiler.DecompilerSettings; 20import com.strobel.decompiler.DecompilerSettings;
22import com.strobel.decompiler.PlainTextOutput;
23import com.strobel.decompiler.languages.java.JavaOutputVisitor;
24import com.strobel.decompiler.languages.java.ast.AstBuilder;
25import com.strobel.decompiler.languages.java.ast.CompilationUnit; 21import com.strobel.decompiler.languages.java.ast.CompilationUnit;
26import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 22import cuchaz.enigma.analysis.EntryReference;
27import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 23import cuchaz.enigma.analysis.IndexTreeBuilder;
28import cuchaz.enigma.analysis.*; 24import cuchaz.enigma.analysis.ParsedJar;
29import cuchaz.enigma.analysis.index.JarIndex; 25import cuchaz.enigma.analysis.index.JarIndex;
30import cuchaz.enigma.api.EnigmaPlugin; 26import cuchaz.enigma.api.EnigmaPlugin;
27import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
28import cuchaz.enigma.translation.Translatable;
29import cuchaz.enigma.translation.Translator;
31import cuchaz.enigma.translation.mapping.*; 30import cuchaz.enigma.translation.mapping.*;
32import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; 31import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
33import cuchaz.enigma.translation.mapping.tree.EntryTree; 32import cuchaz.enigma.translation.mapping.tree.EntryTree;
34import cuchaz.enigma.translation.representation.ReferencedEntryPool;
35import cuchaz.enigma.translation.representation.entry.ClassEntry; 33import cuchaz.enigma.translation.representation.entry.ClassEntry;
36import cuchaz.enigma.translation.representation.entry.Entry; 34import cuchaz.enigma.translation.representation.entry.Entry;
37import cuchaz.enigma.translation.representation.entry.MethodEntry; 35import cuchaz.enigma.translation.representation.entry.MethodEntry;
38import cuchaz.enigma.utils.Utils; 36import org.objectweb.asm.ClassVisitor;
39import oml.ast.transformers.*;
40import org.objectweb.asm.ClassWriter; 37import org.objectweb.asm.ClassWriter;
38import org.objectweb.asm.Opcodes;
41import org.objectweb.asm.tree.ClassNode; 39import org.objectweb.asm.tree.ClassNode;
42 40
43import java.io.*; 41import java.io.File;
42import java.io.FileOutputStream;
43import java.io.IOException;
44import java.io.Writer;
45import java.nio.file.Files;
46import java.nio.file.Path;
44import java.util.*; 47import java.util.*;
45import java.util.concurrent.ConcurrentHashMap; 48import java.util.concurrent.ConcurrentHashMap;
46import java.util.concurrent.atomic.AtomicInteger; 49import java.util.concurrent.atomic.AtomicInteger;
@@ -53,11 +56,12 @@ import java.util.stream.Collectors;
53public class Deobfuscator { 56public class Deobfuscator {
54 57
55 private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class); 58 private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class);
56 private final ReferencedEntryPool entryPool = new ReferencedEntryPool();
57 private final ParsedJar parsedJar; 59 private final ParsedJar parsedJar;
58 private final DecompilerSettings settings;
59 private final JarIndex jarIndex; 60 private final JarIndex jarIndex;
60 private final IndexTreeBuilder indexTreeBuilder; 61 private final IndexTreeBuilder indexTreeBuilder;
62
63 private final SourceProvider obfSourceProvider;
64
61 private EntryRemapper mapper; 65 private EntryRemapper mapper;
62 66
63 public Deobfuscator(ParsedJar jar, Consumer<String> listener) { 67 public Deobfuscator(ParsedJar jar, Consumer<String> listener) {
@@ -75,15 +79,8 @@ public class Deobfuscator {
75 this.indexTreeBuilder = new IndexTreeBuilder(jarIndex); 79 this.indexTreeBuilder = new IndexTreeBuilder(jarIndex);
76 80
77 listener.accept("Preparing..."); 81 listener.accept("Preparing...");
78 // config the decompiler 82
79 this.settings = DecompilerSettings.javaDefaults(); 83 this.obfSourceProvider = new SourceProvider(SourceProvider.createSettings(), new CompiledSourceTypeLoader(parsedJar));
80 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
81 this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
82 this.settings.setForceExplicitTypeArguments(
83 Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
84 // DEBUG
85 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
86 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
87 84
88 // init mappings 85 // init mappings
89 mapper = new EntryRemapper(jarIndex); 86 mapper = new EntryRemapper(jarIndex);
@@ -93,7 +90,7 @@ public class Deobfuscator {
93 this(new ParsedJar(jar), listener); 90 this(new ParsedJar(jar), listener);
94 } 91 }
95 92
96 public Deobfuscator(ParsedJar jar) throws IOException { 93 public Deobfuscator(ParsedJar jar) {
97 this(jar, (msg) -> { 94 this(jar, (msg) -> {
98 }); 95 });
99 } 96 }
@@ -128,9 +125,9 @@ public class Deobfuscator {
128 Collection<Entry<?>> dropped = dropMappings(mappings); 125 Collection<Entry<?>> dropped = dropMappings(mappings);
129 mapper = new EntryRemapper(jarIndex, mappings); 126 mapper = new EntryRemapper(jarIndex, mappings);
130 127
131 DeltaTrackingTree<EntryMapping> deobfToObf = mapper.getDeobfToObf(); 128 DeltaTrackingTree<EntryMapping> obfToDeobf = mapper.getObfToDeobf();
132 for (Entry<?> entry : dropped) { 129 for (Entry<?> entry : dropped) {
133 deobfToObf.trackDeletion(entry); 130 obfToDeobf.trackDeletion(entry);
134 } 131 }
135 } else { 132 } else {
136 mapper = new EntryRemapper(jarIndex); 133 mapper = new EntryRemapper(jarIndex);
@@ -161,7 +158,7 @@ public class Deobfuscator {
161 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); 158 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry);
162 if (!deobfClassEntry.equals(obfClassEntry)) { 159 if (!deobfClassEntry.equals(obfClassEntry)) {
163 // if the class has a mapping, clearly it's deobfuscated 160 // if the class has a mapping, clearly it's deobfuscated
164 deobfClasses.add(deobfClassEntry); 161 deobfClasses.add(obfClassEntry);
165 } else if (obfClassEntry.getPackageName() != null) { 162 } else if (obfClassEntry.getPackageName() != null) {
166 // also call it deobufscated if it's not in the none package 163 // also call it deobufscated if it's not in the none package
167 deobfClasses.add(obfClassEntry); 164 deobfClasses.add(obfClassEntry);
@@ -172,151 +169,126 @@ public class Deobfuscator {
172 } 169 }
173 } 170 }
174 171
175 public TranslatingTypeLoader createTypeLoader() { 172 public SourceProvider getObfSourceProvider() {
176 return new TranslatingTypeLoader( 173 return obfSourceProvider;
177 this.parsedJar,
178 this.jarIndex,
179 this.entryPool,
180 this.mapper.getObfuscator(),
181 this.mapper.getDeobfuscator()
182 );
183 }
184
185 public CompilationUnit getSourceTree(String className) {
186 return getSourceTree(className, createTypeLoader());
187 } 174 }
188 175
189 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { 176 public void writeSources(Path outputDirectory, ProgressListener progress) {
190 return getSourceTree(className, loader, new NoRetryMetadataSystem(loader)); 177 // get the classes to decompile
191 } 178 Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses();
192 179
193 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader, MetadataSystem metadataSystem) { 180 Stopwatch stopwatch = Stopwatch.createStarted();
194 181
195 // we don't know if this class name is obfuscated or deobfuscated 182 try {
196 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 183 Translator deobfuscator = mapper.getDeobfuscator();
197 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
198 184
199 String deobfClassName = mapper.deobfuscate(new ClassEntry(className)).getFullName(); 185 // deobfuscate everything first
186 Map<String, ClassNode> translatedNodes = deobfuscateClasses(progress, classEntries, deobfuscator);
200 187
201 // set the desc loader 188 decompileClasses(outputDirectory, progress, translatedNodes);
202 this.settings.setTypeLoader(loader); 189 } finally {
190 stopwatch.stop();
203 191
204 // see if procyon can find the desc 192 System.out.println("writeSources Done in : " + stopwatch.toString());
205 TypeReference type = metadataSystem.lookupType(deobfClassName);
206 if (type == null) {
207 throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s",
208 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
209 ));
210 } 193 }
211 TypeDefinition resolvedType = type.resolve();
212
213 // decompile it!
214 DecompilerContext context = new DecompilerContext();
215 context.setCurrentType(resolvedType);
216 context.setSettings(this.settings);
217 AstBuilder builder = new AstBuilder(context);
218 builder.addType(resolvedType);
219 builder.runTransformations(null);
220 runCustomTransforms(builder, context);
221 return builder.getCompilationUnit();
222 } 194 }
223 195
224 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 196 private Map<String, ClassNode> deobfuscateClasses(ProgressListener progress, Collection<ClassEntry> classEntries, Translator translator) {
225 return getSourceIndex(sourceTree, source, true); 197 AtomicInteger count = new AtomicInteger();
226 } 198 if (progress != null) {
227 199 progress.init(classEntries.size(), "Deobfuscating classes...");
228 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, boolean ignoreBadTokens) {
229
230 // build the source index
231 SourceIndex index = new SourceIndex(source, ignoreBadTokens);
232 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index);
233
234 EntryResolver resolver = mapper.getDeobfResolver();
235
236 Collection<Token> tokens = Lists.newArrayList(index.referenceTokens());
237
238 // resolve all the classes in the source references
239 for (Token token : tokens) {
240 EntryReference<Entry<?>, Entry<?>> deobfReference = index.getDeobfReference(token);
241 index.replaceDeobfReference(token, resolver.resolveFirstReference(deobfReference, ResolutionStrategy.RESOLVE_CLOSEST));
242 } 200 }
243 201
244 return index; 202 return classEntries.parallelStream()
245 } 203 .map(entry -> {
204 ClassEntry translatedEntry = translator.translate(entry);
205 if (progress != null) {
206 progress.step(count.getAndIncrement(), translatedEntry.toString());
207 }
208
209 ClassNode node = parsedJar.getClassNode(entry.getFullName());
210 if (node != null) {
211 ClassNode translatedNode = new ClassNode();
212 node.accept(new TranslationClassVisitor(translator, Opcodes.ASM5, translatedNode));
213 return translatedNode;
214 }
246 215
247 public String getSource(CompilationUnit sourceTree) { 216 return null;
248 // render the AST into source 217 })
249 StringWriter buf = new StringWriter(); 218 .filter(Objects::nonNull)
250 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); 219 .collect(Collectors.toMap(n -> n.name, Functions.identity()));
251 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null);
252 return buf.toString();
253 } 220 }
254 221
255 public void writeSources(File dirOut, ProgressListener progress) { 222 private void decompileClasses(Path outputDirectory, ProgressListener progress, Map<String, ClassNode> translatedClasses) {
256 // get the classes to decompile 223 Collection<ClassNode> decompileClasses = translatedClasses.values().stream()
257 Set<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses().stream() 224 .filter(classNode -> classNode.name.indexOf('$') == -1)
258 .filter(classEntry -> !classEntry.isInnerClass()) 225 .collect(Collectors.toList());
259 .collect(Collectors.toSet());
260 226
261 if (progress != null) { 227 if (progress != null) {
262 progress.init(classEntries.size(), "Decompiling classes..."); 228 progress.init(decompileClasses.size(), "Decompiling classes...");
263 } 229 }
264 230
265 //create a common instance outside the loop as mappings shouldn't be changing while this is happening 231 //create a common instance outside the loop as mappings shouldn't be changing while this is happening
266 //synchronized to make sure the parallelStream doesn't CME with the cache 232 //synchronized to make sure the parallelStream doesn't CME with the cache
267 ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); 233 ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(translatedClasses::get));
268 234
269 MetadataSystem metadataSystem = new NoRetryMetadataSystem(typeLoader); 235 MetadataSystem metadataSystem = new Deobfuscator.NoRetryMetadataSystem(typeLoader);
270 metadataSystem.setEagerMethodLoadingEnabled(true);//ensures methods are loaded on classload and prevents race conditions 236
237 //ensures methods are loaded on classload and prevents race conditions
238 metadataSystem.setEagerMethodLoadingEnabled(true);
239
240 DecompilerSettings settings = SourceProvider.createSettings();
241 SourceProvider sourceProvider = new SourceProvider(settings, typeLoader, metadataSystem);
271 242
272 // DEOBFUSCATE ALL THE THINGS!! @_@
273 Stopwatch stopwatch = Stopwatch.createStarted();
274 AtomicInteger count = new AtomicInteger(); 243 AtomicInteger count = new AtomicInteger();
275 classEntries.parallelStream().forEach(obfClassEntry -> { 244
276 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); 245 decompileClasses.parallelStream().forEach(translatedNode -> {
277 if (progress != null) { 246 if (progress != null) {
278 progress.step(count.getAndIncrement(), deobfClassEntry.toString()); 247 progress.step(count.getAndIncrement(), translatedNode.name);
279 } 248 }
280 249
281 try { 250 decompileClass(outputDirectory, translatedNode, sourceProvider);
282 // get the source 251 });
283 CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader, metadataSystem); 252 }
284 253
285 // write the file 254 private void decompileClass(Path outputDirectory, ClassNode translatedNode, SourceProvider sourceProvider) {
286 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); 255 try {
287 file.getParentFile().mkdirs(); 256 // get the source
288 try (Writer writer = new BufferedWriter(new FileWriter(file))) { 257 CompilationUnit sourceTree = sourceProvider.getSources(translatedNode.name);
289 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); 258
290 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); 259 Path path = outputDirectory.resolve(translatedNode.name.replace('.', '/') + ".java");
291 } 260 Files.createDirectories(path.getParent());
292 } catch (Throwable t) { 261
293 // don't crash the whole world here, just log the error and keep going 262 try (Writer writer = Files.newBufferedWriter(path)) {
294 // TODO: set up logback via log4j 263 sourceProvider.writeSource(writer, sourceTree);
295 System.err.println("Unable to decompile class " + deobfClassEntry + " (" + obfClassEntry + ")");
296 t.printStackTrace(System.err);
297 } 264 }
298 }); 265 } catch (Throwable t) {
299 stopwatch.stop(); 266 // don't crash the whole world here, just log the error and keep going
300 System.out.println("writeSources Done in : " + stopwatch.toString()); 267 // TODO: set up logback via log4j
301 if (progress != null) { 268 System.err.println("Unable to decompile class " + translatedNode.name);
302 progress.step(count.get(), "Done:"); 269 t.printStackTrace(System.err);
303 } 270 }
304 } 271 }
305 272
306 public void writeJar(File out, ProgressListener progress) { 273 public void writeTransformedJar(File out, ProgressListener progress) {
307 transformJar(out, progress, createTypeLoader()::transformInto); 274 Translator deobfuscator = mapper.getDeobfuscator();
275 writeTransformedJar(out, progress, (node, visitor) -> {
276 ClassEntry entry = new ClassEntry(node.name);
277 node.accept(new TranslationClassVisitor(deobfuscator, Opcodes.ASM5, visitor));
278 return deobfuscator.translate(entry).getFullName();
279 });
308 } 280 }
309 281
310 public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { 282 public void writeTransformedJar(File out, ProgressListener progress, ClassTransformer transformer) {
311 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { 283 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
312 if (progress != null) { 284 if (progress != null) {
313 progress.init(parsedJar.getClassCount(), "Transforming classes..."); 285 progress.init(parsedJar.getClassCount(), "Transforming classes...");
314 } 286 }
315 287
316 AtomicInteger i = new AtomicInteger(); 288 AtomicInteger count = new AtomicInteger();
317 parsedJar.visitNode(node -> { 289 parsedJar.visitNode(node -> {
318 if (progress != null) { 290 if (progress != null) {
319 progress.step(i.getAndIncrement(), node.name); 291 progress.step(count.getAndIncrement(), node.name);
320 } 292 }
321 293
322 try { 294 try {
@@ -329,10 +301,6 @@ public class Deobfuscator {
329 throw new Error("Unable to transform class " + node.name, t); 301 throw new Error("Unable to transform class " + node.name, t);
330 } 302 }
331 }); 303 });
332
333 if (progress != null) {
334 progress.step(i.get(), "Done!");
335 }
336 } catch (IOException ex) { 304 } catch (IOException ex) {
337 throw new Error("Unable to write to Jar file!"); 305 throw new Error("Unable to write to Jar file!");
338 } 306 }
@@ -355,7 +323,7 @@ public class Deobfuscator {
355 } 323 }
356 } 324 }
357 325
358 public boolean isObfuscatedIdentifier(Entry<?> obfEntry) { 326 public boolean isRenamable(Entry<?> obfEntry) {
359 if (obfEntry instanceof MethodEntry) { 327 if (obfEntry instanceof MethodEntry) {
360 // HACKHACK: Object methods are not obfuscated identifiers 328 // HACKHACK: Object methods are not obfuscated identifiers
361 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 329 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
@@ -389,12 +357,15 @@ public class Deobfuscator {
389 return this.jarIndex.getEntryIndex().hasEntry(obfEntry); 357 return this.jarIndex.getEntryIndex().hasEntry(obfEntry);
390 } 358 }
391 359
392 public boolean isRenameable(EntryReference<Entry<?>, Entry<?>> obfReference) { 360 public boolean isRenamable(EntryReference<Entry<?>, Entry<?>> obfReference) {
393 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); 361 return obfReference.isNamed() && isRenamable(obfReference.getNameableEntry());
394 } 362 }
395 363
396 public boolean hasDeobfuscatedName(Entry<?> obfEntry) { 364 public boolean isRemapped(Entry<?> entry) {
397 return mapper.hasDeobfMapping(obfEntry); 365 EntryResolver resolver = mapper.getObfResolver();
366 DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf();
367 return resolver.resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT).stream()
368 .anyMatch(mappings::contains);
398 } 369 }
399 370
400 public void rename(Entry<?> obfEntry, String newName) { 371 public void rename(Entry<?> obfEntry, String newName) {
@@ -409,21 +380,12 @@ public class Deobfuscator {
409 mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName())); 380 mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName()));
410 } 381 }
411 382
412 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context) { 383 public <T extends Translatable> T deobfuscate(T translatable) {
413 List<IAstTransform> transformers = Arrays.asList( 384 return mapper.deobfuscate(translatable);
414 new ObfuscatedEnumSwitchRewriterTransform(context),
415 new VarargsFixer(context),
416 new RemoveObjectCasts(context),
417 new Java8Generics(),
418 new InvalidIdentifierFix()
419 );
420 for (IAstTransform transform : transformers) {
421 transform.run(builder.getCompilationUnit());
422 }
423 } 385 }
424 386
425 public interface ClassTransformer { 387 public interface ClassTransformer {
426 String transform(ClassNode node, ClassWriter writer); 388 String transform(ClassNode node, ClassVisitor visitor);
427 } 389 }
428 390
429 public static class NoRetryMetadataSystem extends MetadataSystem { 391 public static class NoRetryMetadataSystem extends MetadataSystem {
@@ -448,6 +410,7 @@ public class Deobfuscator {
448 return result; 410 return result;
449 } 411 }
450 412
413 @Override
451 public synchronized TypeDefinition resolve(final TypeReference type) { 414 public synchronized TypeDefinition resolve(final TypeReference type) {
452 return super.resolve(type); 415 return super.resolve(type);
453 } 416 }
diff --git a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
deleted file mode 100644
index 24822dd3..00000000
--- a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
+++ /dev/null
@@ -1,19 +0,0 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import cuchaz.enigma.translation.representation.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/SourceProvider.java b/src/main/java/cuchaz/enigma/SourceProvider.java
new file mode 100644
index 00000000..48e5a590
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/SourceProvider.java
@@ -0,0 +1,97 @@
1package cuchaz.enigma;
2
3import com.strobel.assembler.metadata.ITypeLoader;
4import com.strobel.assembler.metadata.MetadataSystem;
5import com.strobel.assembler.metadata.TypeDefinition;
6import com.strobel.assembler.metadata.TypeReference;
7import com.strobel.decompiler.DecompilerContext;
8import com.strobel.decompiler.DecompilerSettings;
9import com.strobel.decompiler.PlainTextOutput;
10import com.strobel.decompiler.languages.java.JavaOutputVisitor;
11import com.strobel.decompiler.languages.java.ast.AstBuilder;
12import com.strobel.decompiler.languages.java.ast.CompilationUnit;
13import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
14import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
15import cuchaz.enigma.utils.Utils;
16import oml.ast.transformers.*;
17
18import java.io.StringWriter;
19import java.io.Writer;
20import java.util.Arrays;
21import java.util.List;
22
23public class SourceProvider {
24 private final DecompilerSettings settings;
25
26 private final ITypeLoader typeLoader;
27 private final MetadataSystem metadataSystem;
28
29 public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader, MetadataSystem metadataSystem) {
30 this.settings = settings;
31 this.typeLoader = typeLoader;
32 this.metadataSystem = metadataSystem;
33 }
34
35 public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader) {
36 this(settings, typeLoader, new Deobfuscator.NoRetryMetadataSystem(typeLoader));
37 }
38
39 public static DecompilerSettings createSettings() {
40 DecompilerSettings settings = DecompilerSettings.javaDefaults();
41 settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
42 settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true));
43 settings.setForceExplicitTypeArguments(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true));
44 settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
45 settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
46
47 return settings;
48 }
49
50 public CompilationUnit getSources(String name) {
51 TypeReference type = metadataSystem.lookupType(name);
52 if (type == null) {
53 throw new Error(String.format("Unable to find desc: %s", name));
54 }
55
56 TypeDefinition resolvedType = type.resolve();
57
58 settings.setTypeLoader(typeLoader);
59
60 // decompile it!
61 DecompilerContext context = new DecompilerContext();
62 context.setCurrentType(resolvedType);
63 context.setSettings(settings);
64
65 AstBuilder builder = new AstBuilder(context);
66 builder.addType(resolvedType);
67 builder.runTransformations(null);
68 runCustomTransforms(builder, context);
69
70 return builder.getCompilationUnit();
71 }
72
73 public void writeSource(Writer writer, CompilationUnit sourceTree) {
74 // render the AST into source
75 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
76 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null);
77 }
78
79 public String writeSourceToString(CompilationUnit sourceTree) {
80 StringWriter writer = new StringWriter();
81 writeSource(writer, sourceTree);
82 return writer.toString();
83 }
84
85 private static void runCustomTransforms(AstBuilder builder, DecompilerContext context) {
86 List<IAstTransform> transformers = Arrays.asList(
87 new ObfuscatedEnumSwitchRewriterTransform(context),
88 new VarargsFixer(context),
89 new RemoveObjectCasts(context),
90 new Java8Generics(),
91 new InvalidIdentifierFix()
92 );
93 for (IAstTransform transform : transformers) {
94 transform.run(builder.getCompilationUnit());
95 }
96 }
97}
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
index 657bee42..f6eee690 100644
--- a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
@@ -1,38 +1,19 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import com.strobel.assembler.metadata.Buffer; 3import com.strobel.assembler.metadata.Buffer;
4import cuchaz.enigma.translation.representation.entry.ClassEntry; 4import com.strobel.assembler.metadata.ITypeLoader;
5import org.objectweb.asm.ClassWriter;
6import org.objectweb.asm.tree.ClassNode;
7
8import java.util.List;
9 5
10/** 6/**
11 * Typeloader with synchronized tryLoadType method 7 * Typeloader with synchronized tryLoadType method
12 */ 8 */
13public class SynchronizedTypeLoader implements ITranslatingTypeLoader { 9public class SynchronizedTypeLoader implements ITypeLoader {
14 private final TranslatingTypeLoader delegate; 10 private final ITypeLoader delegate;
15 11
16 public SynchronizedTypeLoader(TranslatingTypeLoader delegate) { 12 public SynchronizedTypeLoader(ITypeLoader delegate) {
17 this.delegate = delegate; 13 this.delegate = delegate;
18 } 14 }
19 15
20 @Override 16 @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) { 17 public synchronized boolean tryLoadType(String internalName, Buffer buffer) {
37 return delegate.tryLoadType(internalName, buffer); 18 return delegate.tryLoadType(internalName, buffer);
38 } 19 }
diff --git a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java b/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java
new file mode 100644
index 00000000..991e91d4
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java
@@ -0,0 +1,33 @@
1package cuchaz.enigma.analysis;
2
3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
5import com.strobel.decompiler.languages.java.ast.ImportDeclaration;
6import com.strobel.decompiler.languages.java.ast.PackageDeclaration;
7import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
8
9public final class DropImportAstTransform implements IAstTransform {
10 public static final DropImportAstTransform INSTANCE = new DropImportAstTransform();
11
12 private DropImportAstTransform() {
13 }
14
15 @Override
16 public void run(AstNode compilationUnit) {
17 compilationUnit.acceptVisitor(new Visitor(), null);
18 }
19
20 static class Visitor extends DepthFirstAstVisitor<Void, Void> {
21 @Override
22 public Void visitPackageDeclaration(PackageDeclaration node, Void data) {
23 node.remove();
24 return null;
25 }
26
27 @Override
28 public Void visitImportDeclaration(ImportDeclaration node, Void data) {
29 node.remove();
30 return null;
31 }
32 }
33}
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
index ad3aceb0..ddcda3ed 100644
--- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -12,9 +12,12 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.io.ByteStreams; 14import com.google.common.io.ByteStreams;
15import cuchaz.enigma.CompiledSource;
16import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor;
15import cuchaz.enigma.translation.representation.entry.ClassEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import org.objectweb.asm.ClassReader; 18import org.objectweb.asm.ClassReader;
17import org.objectweb.asm.ClassVisitor; 19import org.objectweb.asm.ClassVisitor;
20import org.objectweb.asm.Opcodes;
18import org.objectweb.asm.tree.ClassNode; 21import org.objectweb.asm.tree.ClassNode;
19 22
20import javax.annotation.Nullable; 23import javax.annotation.Nullable;
@@ -28,12 +31,12 @@ import java.util.jar.JarEntry;
28import java.util.jar.JarFile; 31import java.util.jar.JarFile;
29import java.util.jar.JarInputStream; 32import java.util.jar.JarInputStream;
30 33
31public class ParsedJar { 34public class ParsedJar implements CompiledSource {
32 private final Map<String, byte[]> classBytes; 35 private final Map<String, byte[]> classBytes;
33 private final Map<String, ClassNode> nodeCache = new HashMap<>(); 36 private final Map<String, ClassNode> nodeCache = new HashMap<>();
34 37
35 public ParsedJar(JarFile jar) throws IOException { 38 public ParsedJar(JarFile jar) throws IOException {
36 Map<String, byte[]> uClassBytes = new LinkedHashMap<>();; 39 Map<String, byte[]> uClassBytes = new LinkedHashMap<>();
37 try { 40 try {
38 // get the jar entries that correspond to classes 41 // get the jar entries that correspond to classes
39 Enumeration<JarEntry> entries = jar.entries(); 42 Enumeration<JarEntry> entries = jar.entries();
@@ -93,29 +96,34 @@ public class ParsedJar {
93 return classBytes.size(); 96 return classBytes.size();
94 } 97 }
95 98
96 public List<ClassEntry> getClassEntries() {
97 List<ClassEntry> entries = new ArrayList<>(classBytes.size());
98 for (String s : classBytes.keySet()) {
99 entries.add(new ClassEntry(s));
100 }
101 return entries;
102 }
103
104 @Nullable 99 @Nullable
100 @Override
105 public ClassNode getClassNode(String name) { 101 public ClassNode getClassNode(String name) {
106 return nodeCache.computeIfAbsent(name, (n) -> { 102 return nodeCache.computeIfAbsent(name, (n) -> {
107 byte[] bytes = classBytes.get(name); 103 byte[] bytes = classBytes.get(name);
108 if (bytes == null) { 104 if (bytes == null) {
109 return null; 105 return null;
110 } 106 }
107
111 ClassReader reader = new ClassReader(bytes); 108 ClassReader reader = new ClassReader(bytes);
112 ClassNode node = new ClassNode(); 109 ClassNode node = new ClassNode();
113 reader.accept(node, 0); 110
111 LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Opcodes.ASM5, node);
112 reader.accept(visitor, 0);
113
114 return node; 114 return node;
115 }); 115 });
116 } 116 }
117 117
118 public Map<String, byte[]> getClassDataMap() { 118 public List<ClassEntry> getClassEntries() {
119 List<ClassEntry> entries = new ArrayList<>(classBytes.size());
120 for (String s : classBytes.keySet()) {
121 entries.add(new ClassEntry(s));
122 }
123 return entries;
124 }
125
126 public Map<String, byte[]> getClassDataMap() {
119 return classBytes; 127 return classBytes;
120 } 128 }
121} 129}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index abdec92e..ed12ce37 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -11,17 +11,24 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.*; 14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap;
15import com.strobel.decompiler.languages.Region; 18import com.strobel.decompiler.languages.Region;
16import com.strobel.decompiler.languages.java.ast.AstNode; 19import com.strobel.decompiler.languages.java.ast.*;
17import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; 20import cuchaz.enigma.gui.SourceRemapper;
18import com.strobel.decompiler.languages.java.ast.Identifier; 21import cuchaz.enigma.translation.mapping.EntryResolver;
19import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 22import cuchaz.enigma.translation.mapping.ResolutionStrategy;
20import cuchaz.enigma.translation.representation.entry.Entry; 23import cuchaz.enigma.translation.representation.entry.Entry;
21 24
22import javax.annotation.Nullable; 25import javax.annotation.Nullable;
23import java.util.*; 26import java.util.Collection;
27import java.util.List;
28import java.util.Map;
29import java.util.TreeMap;
24import java.util.regex.Pattern; 30import java.util.regex.Pattern;
31import java.util.stream.Collectors;
25 32
26public class SourceIndex { 33public class SourceIndex {
27 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); 34 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
@@ -46,6 +53,13 @@ public class SourceIndex {
46 calculateLineOffsets(); 53 calculateLineOffsets();
47 } 54 }
48 55
56 public static SourceIndex buildIndex(String sourceString, CompilationUnit sourceTree, boolean ignoreBadTokens) {
57 SourceIndex index = new SourceIndex(sourceString, ignoreBadTokens);
58 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
59
60 return index;
61 }
62
49 private void calculateLineOffsets() { 63 private void calculateLineOffsets() {
50 // count the lines 64 // count the lines
51 this.lineOffsets = Lists.newArrayList(); 65 this.lineOffsets = Lists.newArrayList();
@@ -57,32 +71,29 @@ public class SourceIndex {
57 } 71 }
58 } 72 }
59 73
60 public void remap(String source, Map<Token, Token> tokenMap) { 74 public SourceIndex remapTo(SourceRemapper.Result result) {
61 this.source = source; 75 SourceIndex remapped = new SourceIndex(result.getSource(), ignoreBadTokens);
62 calculateLineOffsets();
63 76
64 for (Entry<?> entry : Lists.newArrayList(declarationToToken.keySet())) { 77 for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) {
65 Token token = declarationToToken.get(entry); 78 remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue()));
66 declarationToToken.put(entry, tokenMap.getOrDefault(token, token));
67 } 79 }
68 80
69 for (EntryReference<Entry<?>, Entry<?>> ref : referenceToTokens.keySet()) { 81 for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) {
70 Collection<Token> oldTokens = referenceToTokens.get(ref); 82 EntryReference<Entry<?>, Entry<?>> reference = entry.getKey();
71 List<Token> newTokens = new ArrayList<>(oldTokens.size()); 83 Collection<Token> oldTokens = entry.getValue();
72 84
73 for (Token token : oldTokens) { 85 Collection<Token> newTokens = oldTokens.stream()
74 newTokens.add(tokenMap.getOrDefault(token, token)); 86 .map(result::getRemappedToken)
75 } 87 .collect(Collectors.toList());
76 88
77 referenceToTokens.replaceValues(ref, newTokens); 89 remapped.referenceToTokens.putAll(reference, newTokens);
78 } 90 }
79 91
80 TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReferenceCopy = new TreeMap<>(tokenToReference); 92 for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) {
81 93 remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue());
82 tokenToReference.clear();
83 for (Token token : tokenToReferenceCopy.keySet()) {
84 tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token));
85 } 94 }
95
96 return remapped;
86 } 97 }
87 98
88 public String getSource() { 99 public String getSource() {
@@ -164,20 +175,13 @@ public class SourceIndex {
164 } 175 }
165 176
166 @Nullable 177 @Nullable
167 public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { 178 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
168 if (token == null) { 179 if (token == null) {
169 return null; 180 return null;
170 } 181 }
171 return this.tokenToReference.get(token); 182 return this.tokenToReference.get(token);
172 } 183 }
173 184
174 public void replaceDeobfReference(Token token, EntryReference<Entry<?>, Entry<?>> newDeobfReference) {
175 EntryReference<Entry<?>, Entry<?>> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference);
176
177 Collection<Token> tokens = this.referenceToTokens.removeAll(oldDeobfReferences);
178 this.referenceToTokens.putAll(newDeobfReference, tokens);
179 }
180
181 public Iterable<Token> referenceTokens() { 185 public Iterable<Token> referenceTokens() {
182 return this.tokenToReference.keySet(); 186 return this.tokenToReference.keySet();
183 } 187 }
@@ -190,8 +194,8 @@ public class SourceIndex {
190 return this.declarationToToken.keySet(); 194 return this.declarationToToken.keySet();
191 } 195 }
192 196
193 public Token getDeclarationToken(Entry<?> deobfEntry) { 197 public Token getDeclarationToken(Entry<?> entry) {
194 return this.declarationToToken.get(deobfEntry); 198 return this.declarationToToken.get(entry);
195 } 199 }
196 200
197 public int getLineNumber(int pos) { 201 public int getLineNumber(int pos) {
@@ -215,4 +219,18 @@ public class SourceIndex {
215 // line and col are 1-based 219 // line and col are 1-based
216 return this.lineOffsets.get(line - 1) + col - 1; 220 return this.lineOffsets.get(line - 1) + col - 1;
217 } 221 }
222
223 public void resolveReferences(EntryResolver resolver) {
224 // resolve all the classes in the source references
225 for (Token token : Lists.newArrayList(referenceToTokens.values())) {
226 EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token);
227 EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
228
229 // replace the reference
230 tokenToReference.replace(token, resolvedReference);
231
232 Collection<Token> tokens = referenceToTokens.removeAll(reference);
233 referenceToTokens.putAll(resolvedReference, tokens);
234 }
235 }
218} 236}
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index 486603ce..a4fe9ee9 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -18,21 +18,17 @@ import 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.translation.representation.ProcyonEntryFactory; 20import cuchaz.enigma.translation.representation.ProcyonEntryFactory;
21import cuchaz.enigma.translation.representation.ReferencedEntryPool;
22import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 21import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
23import cuchaz.enigma.translation.representation.entry.ClassEntry; 22import cuchaz.enigma.translation.representation.entry.ClassEntry;
24import cuchaz.enigma.translation.representation.entry.FieldDefEntry; 23import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
25import cuchaz.enigma.translation.representation.entry.MethodDefEntry; 24import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
26 25
27public class SourceIndexClassVisitor extends SourceIndexVisitor { 26public class SourceIndexClassVisitor extends SourceIndexVisitor {
28 private final ReferencedEntryPool entryPool;
29 private final ProcyonEntryFactory entryFactory; 27 private final ProcyonEntryFactory entryFactory;
30 private ClassDefEntry classEntry; 28 private ClassDefEntry classEntry;
31 29
32 public SourceIndexClassVisitor(ReferencedEntryPool entryPool, ClassDefEntry classEntry) { 30 public SourceIndexClassVisitor(ClassDefEntry classEntry) {
33 super(entryPool); 31 this.entryFactory = new ProcyonEntryFactory();
34 this.entryPool = entryPool;
35 this.entryFactory = new ProcyonEntryFactory(entryPool);
36 this.classEntry = classEntry; 32 this.classEntry = classEntry;
37 } 33 }
38 34
@@ -44,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
44 if (!classEntry.equals(this.classEntry)) { 40 if (!classEntry.equals(this.classEntry)) {
45 // it's a subtype, recurse 41 // it's a subtype, recurse
46 index.addDeclaration(node.getNameToken(), classEntry); 42 index.addDeclaration(node.getNameToken(), classEntry);
47 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); 43 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
48 } 44 }
49 45
50 return recurse(node, index); 46 return recurse(node, index);
@@ -71,7 +67,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
71 tokenNode = node.getModifiers().firstOrNullObject(); 67 tokenNode = node.getModifiers().firstOrNullObject();
72 } 68 }
73 index.addDeclaration(tokenNode, methodEntry); 69 index.addDeclaration(tokenNode, methodEntry);
74 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); 70 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
75 } 71 }
76 72
77 @Override 73 @Override
@@ -79,7 +75,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
79 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 75 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
80 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); 76 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
81 index.addDeclaration(node.getNameToken(), methodEntry); 77 index.addDeclaration(node.getNameToken(), methodEntry);
82 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); 78 return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index);
83 } 79 }
84 80
85 @Override 81 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 73db28fb..c4785b67 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -17,8 +17,8 @@ import com.strobel.assembler.metadata.*;
17import com.strobel.decompiler.ast.Variable; 17import com.strobel.decompiler.ast.Variable;
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.translation.representation.MethodDescriptor;
20import cuchaz.enigma.translation.representation.TypeDescriptor; 21import cuchaz.enigma.translation.representation.TypeDescriptor;
21import cuchaz.enigma.translation.representation.*;
22import cuchaz.enigma.translation.representation.entry.*; 22import cuchaz.enigma.translation.representation.entry.*;
23 23
24import java.lang.Error; 24import java.lang.Error;
@@ -26,16 +26,12 @@ import java.util.HashMap;
26import java.util.Map; 26import java.util.Map;
27 27
28public class SourceIndexMethodVisitor extends SourceIndexVisitor { 28public class SourceIndexMethodVisitor extends SourceIndexVisitor {
29 private final ReferencedEntryPool entryPool;
30
31 private final MethodDefEntry methodEntry; 29 private final MethodDefEntry methodEntry;
32 30
33 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 31 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
34 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>(); 32 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>();
35 33
36 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) { 34 public SourceIndexMethodVisitor(MethodDefEntry methodEntry) {
37 super(entryPool);
38 this.entryPool = entryPool;
39 this.methodEntry = methodEntry; 35 this.methodEntry = methodEntry;
40 } 36 }
41 37
@@ -44,10 +40,10 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
44 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 40 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
45 41
46 // get the behavior entry 42 // get the behavior entry
47 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 43 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
48 MethodEntry methodEntry = null; 44 MethodEntry methodEntry = null;
49 if (ref instanceof MethodReference) { 45 if (ref instanceof MethodReference) {
50 methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature()); 46 methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
51 } 47 }
52 if (methodEntry != null) { 48 if (methodEntry != null) {
53 // get the node for the token 49 // get the node for the token
@@ -80,11 +76,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
80 throw new Error("Expected a field here! got " + ref); 76 throw new Error("Expected a field here! got " + ref);
81 } 77 }
82 78
83 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 79 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
84 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); 80 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
85 if (fieldEntry == null) {
86 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
87 }
88 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); 81 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
89 } 82 }
90 83
@@ -95,7 +88,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
95 public Void visitSimpleType(SimpleType node, SourceIndex index) { 88 public Void visitSimpleType(SimpleType node, SourceIndex index) {
96 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); 89 TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE);
97 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { 90 if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) {
98 ClassEntry classEntry = entryPool.getClass(ref.getInternalName()); 91 ClassEntry classEntry = new ClassEntry(ref.getInternalName());
99 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry); 92 index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry);
100 } 93 }
101 94
@@ -108,7 +101,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
108 int parameterIndex = def.getSlot(); 101 int parameterIndex = def.getSlot();
109 102
110 if (parameterIndex >= 0) { 103 if (parameterIndex >= 0) {
111 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, parameterIndex, node.getName(), true); 104 TypeDescriptor parameterType = TypeDescriptor.parse(def.getParameterType());
105 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(methodEntry, parameterIndex, node.getName(), true, parameterType);
112 Identifier identifier = node.getNameToken(); 106 Identifier identifier = node.getNameToken();
113 // cache the argument entry and the identifier 107 // cache the argument entry and the identifier
114 identifierEntryCache.put(identifier.getName(), localVariableEntry); 108 identifierEntryCache.put(identifier.getName(), localVariableEntry);
@@ -122,11 +116,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
122 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { 116 public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) {
123 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 117 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
124 if (ref != null) { 118 if (ref != null) {
125 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 119 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
126 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); 120 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
127 if (fieldEntry == null) {
128 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
129 }
130 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); 121 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
131 } else 122 } else
132 this.checkIdentifier(node, index); 123 this.checkIdentifier(node, index);
@@ -154,13 +145,11 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
154 @Override 145 @Override
155 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 146 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
156 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 147 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
157 if (ref != null) { 148 if (ref != null && node.getType() instanceof SimpleType) {
158 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 149 SimpleType simpleTypeNode = (SimpleType) node.getType();
159 MethodEntry constructorEntry = entryPool.getMethod(classEntry, "<init>", ref.getErasedSignature()); 150 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
160 if (node.getType() instanceof SimpleType) { 151 MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature()));
161 SimpleType simpleTypeNode = (SimpleType) node.getType(); 152 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry);
162 index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry);
163 }
164 } 153 }
165 154
166 return recurse(node, index); 155 return recurse(node, index);
@@ -181,7 +170,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
181 if (originalVariable != null) { 170 if (originalVariable != null) {
182 int variableIndex = originalVariable.getSlot(); 171 int variableIndex = originalVariable.getSlot();
183 if (variableIndex >= 0) { 172 if (variableIndex >= 0) {
184 LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, variableIndex, initializer.getName(), false); 173 TypeDescriptor variableType = TypeDescriptor.parse(originalVariable.getVariableType());
174 LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(methodEntry, variableIndex, initializer.getName(), false, variableType);
185 identifierEntryCache.put(identifier.getName(), localVariableEntry); 175 identifierEntryCache.put(identifier.getName(), localVariableEntry);
186 addDeclarationToUnmatched(identifier.getName(), index); 176 addDeclarationToUnmatched(identifier.getName(), index);
187 index.addDeclaration(identifier, localVariableEntry); 177 index.addDeclaration(identifier, localVariableEntry);
@@ -199,17 +189,19 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
199 189
200 if (ref instanceof MethodReference) { 190 if (ref instanceof MethodReference) {
201 // get the behavior entry 191 // get the behavior entry
202 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 192 ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName());
203 MethodEntry methodEntry = null; 193 MethodEntry methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature()));
204 194
205 methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature());
206 // get the node for the token 195 // get the node for the token
207 AstNode tokenNode = node.getMethodNameToken(); 196 AstNode methodNameToken = node.getMethodNameToken();
208 if (tokenNode == null || (tokenNode.getRegion().getBeginLine() == 0 || tokenNode.getRegion().getEndLine() == 0)){ 197 AstNode targetToken = node.getTarget();
209 tokenNode = node.getTarget(); 198
199 if (methodNameToken != null) {
200 index.addReference(methodNameToken, methodEntry, this.methodEntry);
210 } 201 }
211 if (tokenNode != null) { 202
212 index.addReference(tokenNode, methodEntry, this.methodEntry); 203 if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) {
204 index.addReference(targetToken, methodEntry.getParent(), this.methodEntry);
213 } 205 }
214 } 206 }
215 207
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index 564830c6..75a66a2d 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -14,23 +14,16 @@ 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.translation.representation.ReferencedEntryPool;
18import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 17import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19 18
20public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 19public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
21 private final ReferencedEntryPool entryPool;
22
23 public SourceIndexVisitor(ReferencedEntryPool entryPool) {
24 this.entryPool = entryPool;
25 }
26
27 @Override 20 @Override
28 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 21 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
29 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 22 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
30 ClassDefEntry classEntry = ClassDefEntry.parse(def); 23 ClassDefEntry classEntry = ClassDefEntry.parse(def);
31 index.addDeclaration(node.getNameToken(), classEntry); 24 index.addDeclaration(node.getNameToken(), classEntry);
32 25
33 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); 26 return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index);
34 } 27 }
35 28
36 protected Void recurse(AstNode node, SourceIndex index) { 29 protected Void recurse(AstNode node, SourceIndex index) {
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java
index 14fa7ca4..12e0aa6c 100644
--- a/src/main/java/cuchaz/enigma/analysis/Token.java
+++ b/src/main/java/cuchaz/enigma/analysis/Token.java
@@ -30,12 +30,12 @@ public class Token implements Comparable<Token> {
30 return to.length() - length; 30 return to.length() - length;
31 } 31 }
32 32
33 public String rename(String source, String to) { 33 public void rename(StringBuffer source, String to) {
34 int oldEnd = this.end; 34 int oldEnd = this.end;
35 this.text = to; 35 this.text = to;
36 this.end = this.start + to.length(); 36 this.end = this.start + to.length();
37 37
38 return source.substring(0, this.start) + to + source.substring(oldEnd); 38 source.replace(start, oldEnd, to);
39 } 39 }
40 40
41 public Token move(int offset) { 41 public Token move(int offset) {
@@ -64,7 +64,7 @@ public class Token implements Comparable<Token> {
64 } 64 }
65 65
66 public boolean equals(Token other) { 66 public boolean equals(Token other) {
67 return start == other.start && end == other.end; 67 return start == other.start && end == other.end && text.equals(other.text);
68 } 68 }
69 69
70 @Override 70 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
index e1903d9f..8f6bd462 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -1,7 +1,6 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import com.google.common.collect.Maps; 3import com.google.common.collect.Maps;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryResolver; 4import cuchaz.enigma.translation.mapping.EntryResolver;
6import cuchaz.enigma.translation.representation.AccessFlags; 5import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.entry.MethodEntry; 6import cuchaz.enigma.translation.representation.entry.MethodEntry;
@@ -10,7 +9,7 @@ import javax.annotation.Nullable;
10import java.util.Collection; 9import java.util.Collection;
11import java.util.Map; 10import java.util.Map;
12 11
13public class BridgeMethodIndex implements JarIndexer, RemappableIndex { 12public class BridgeMethodIndex implements JarIndexer {
14 private final EntryIndex entryIndex; 13 private final EntryIndex entryIndex;
15 private final ReferenceIndex referenceIndex; 14 private final ReferenceIndex referenceIndex;
16 15
@@ -22,19 +21,6 @@ public class BridgeMethodIndex implements JarIndexer, RemappableIndex {
22 } 21 }
23 22
24 @Override 23 @Override
25 public void remap(Translator translator) {
26 accessedToBridge = translator.translate(accessedToBridge);
27 }
28
29 @Override
30 public BridgeMethodIndex remapped(Translator translator) {
31 BridgeMethodIndex index = new BridgeMethodIndex(entryIndex, referenceIndex);
32 index.accessedToBridge = translator.translate(accessedToBridge);
33
34 return index;
35 }
36
37 @Override
38 public void processIndex(EntryResolver resolver) { 24 public void processIndex(EntryResolver resolver) {
39 // look for access and bridged methods 25 // look for access and bridged methods
40 for (MethodEntry methodEntry : entryIndex.getMethods()) { 26 for (MethodEntry methodEntry : entryIndex.getMethods()) {
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
index 55bfbc24..773eaf18 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
@@ -1,6 +1,5 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.AccessFlags; 3import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.entry.*; 4import cuchaz.enigma.translation.representation.entry.*;
6 5
@@ -9,29 +8,12 @@ import java.util.Collection;
9import java.util.HashMap; 8import java.util.HashMap;
10import java.util.Map; 9import java.util.Map;
11 10
12public class EntryIndex implements JarIndexer, RemappableIndex { 11public class EntryIndex implements JarIndexer {
13 private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); 12 private Map<ClassEntry, AccessFlags> classes = new HashMap<>();
14 private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); 13 private Map<FieldEntry, AccessFlags> fields = new HashMap<>();
15 private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); 14 private Map<MethodEntry, AccessFlags> methods = new HashMap<>();
16 15
17 @Override 16 @Override
18 public void remap(Translator translator) {
19 classes = translator.translateKeys(classes);
20 fields = translator.translateKeys(fields);
21 methods = translator.translateKeys(methods);
22 }
23
24 @Override
25 public EntryIndex remapped(Translator translator) {
26 EntryIndex index = new EntryIndex();
27 index.classes = translator.translateKeys(classes);
28 index.fields = translator.translateKeys(fields);
29 index.methods = translator.translateKeys(methods);
30
31 return index;
32 }
33
34 @Override
35 public void indexClass(ClassDefEntry classEntry) { 17 public void indexClass(ClassDefEntry classEntry) {
36 classes.put(classEntry, classEntry.getAccess()); 18 classes.put(classEntry, classEntry.getAccess());
37 } 19 }
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
index d165cc83..17bed54c 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -14,7 +14,6 @@ package cuchaz.enigma.analysis.index;
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.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassDefEntry; 17import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.translation.representation.entry.ClassEntry; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
20 19
@@ -22,26 +21,11 @@ import java.util.Collection;
22import java.util.LinkedList; 21import java.util.LinkedList;
23import java.util.Set; 22import java.util.Set;
24 23
25public class InheritanceIndex implements JarIndexer, RemappableIndex { 24public class InheritanceIndex implements JarIndexer {
26 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); 25 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
27 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); 26 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
28 27
29 @Override 28 @Override
30 public void remap(Translator translator) {
31 classChildren = translator.translate(classChildren);
32 classParents = translator.translate(classParents);
33 }
34
35 @Override
36 public InheritanceIndex remapped(Translator translator) {
37 InheritanceIndex index = new InheritanceIndex();
38 index.classParents = translator.translate(classParents);
39 index.classChildren = translator.translate(classChildren);
40
41 return index;
42 }
43
44 @Override
45 public void indexClass(ClassDefEntry classEntry) { 29 public void indexClass(ClassDefEntry classEntry) {
46 ClassEntry superClass = classEntry.getSuperClass(); 30 ClassEntry superClass = classEntry.getSuperClass();
47 if (superClass != null) { 31 if (superClass != null) {
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
index 0880244a..9b21cbae 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -14,7 +14,6 @@ package cuchaz.enigma.analysis.index;
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 cuchaz.enigma.analysis.ParsedJar; 16import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryResolver; 17import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.IndexEntryResolver; 18import cuchaz.enigma.translation.mapping.IndexEntryResolver;
20import cuchaz.enigma.translation.representation.entry.*; 19import cuchaz.enigma.translation.representation.entry.*;
@@ -25,7 +24,7 @@ import java.util.Arrays;
25import java.util.Collection; 24import java.util.Collection;
26import java.util.function.Consumer; 25import java.util.function.Consumer;
27 26
28public class JarIndex implements JarIndexer, RemappableIndex { 27public class JarIndex implements JarIndexer {
29 private final EntryIndex entryIndex; 28 private final EntryIndex entryIndex;
30 private final InheritanceIndex inheritanceIndex; 29 private final InheritanceIndex inheritanceIndex;
31 private final ReferenceIndex referenceIndex; 30 private final ReferenceIndex referenceIndex;
@@ -53,25 +52,6 @@ public class JarIndex implements JarIndexer, RemappableIndex {
53 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); 52 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
54 } 53 }
55 54
56 @Override
57 public void remap(Translator translator) {
58 entryIndex.remap(translator);
59 inheritanceIndex.remap(translator);
60 bridgeMethodIndex.remap(translator);
61 }
62
63 @Override
64 public JarIndex remapped(Translator translator) {
65 EntryIndex entryIndex = this.entryIndex.remapped(translator);
66 InheritanceIndex inheritanceIndex = this.inheritanceIndex.remapped(translator);
67 BridgeMethodIndex bridgeMethodIndex = this.bridgeMethodIndex.remapped(translator);
68
69 JarIndex remappedIndex = new JarIndex(entryIndex, inheritanceIndex, this.referenceIndex, bridgeMethodIndex);
70 remappedIndex.methodImplementations.putAll(methodImplementations);
71
72 return remappedIndex;
73 }
74
75 public void indexJar(ParsedJar jar, Consumer<String> progress) { 55 public void indexJar(ParsedJar jar, Consumer<String> progress) {
76 progress.accept("Indexing entries (1/3)"); 56 progress.accept("Indexing entries (1/3)");
77 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); 57 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
diff --git a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
deleted file mode 100644
index 537e7726..00000000
--- a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
+++ /dev/null
@@ -1,9 +0,0 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4
5public interface RemappableIndex {
6 void remap(Translator translator);
7
8 RemappableIndex remapped(Translator translator);
9}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java
new file mode 100644
index 00000000..16dbba1b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java
@@ -0,0 +1,80 @@
1package cuchaz.enigma.bytecode.translators;
2
3import com.google.common.base.CharMatcher;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
6import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
7import org.objectweb.asm.ClassVisitor;
8import org.objectweb.asm.Label;
9import org.objectweb.asm.MethodVisitor;
10
11import java.util.List;
12
13public class LocalVariableFixVisitor extends ClassVisitor {
14 private ClassDefEntry ownerEntry;
15
16 public LocalVariableFixVisitor(int api, ClassVisitor visitor) {
17 super(api, visitor);
18 }
19
20 @Override
21 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
22 ownerEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
23 super.visit(version, access, name, signature, superName, interfaces);
24 }
25
26 @Override
27 public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
28 MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature);
29 return new Method(api, methodEntry, super.visitMethod(access, name, descriptor, signature, exceptions));
30 }
31
32 private class Method extends MethodVisitor {
33 private final MethodDefEntry methodEntry;
34 private boolean hasLvt;
35
36 Method(int api, MethodDefEntry methodEntry, MethodVisitor visitor) {
37 super(api, visitor);
38 this.methodEntry = methodEntry;
39 }
40
41 @Override
42 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
43 hasLvt = true;
44
45 String translatedName = name;
46
47 if (isInvalidName(name)) {
48 int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index);
49
50 if (argumentIndex >= 0) {
51 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
52 boolean argument = argumentIndex < arguments.size();
53 if (argument) {
54 translatedName = "arg" + (argumentIndex + 1);
55 } else {
56 translatedName = "var" + (argumentIndex + 1);
57 }
58 }
59 }
60
61 super.visitLocalVariable(translatedName, desc, signature, start, end, index);
62 }
63
64 private boolean isInvalidName(String name) {
65 return !CharMatcher.ascii().matchesAllOf(name);
66 }
67
68 @Override
69 public void visitEnd() {
70 if (!hasLvt) {
71 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
72 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) {
73 super.visitParameter("arg" + (argumentIndex + 1), 0);
74 }
75 }
76
77 super.visitEnd();
78 }
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
index 53d09bb6..e4c41d32 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -13,7 +13,6 @@ package cuchaz.enigma.bytecode.translators;
13 13
14import cuchaz.enigma.translation.Translator; 14import cuchaz.enigma.translation.Translator;
15import cuchaz.enigma.translation.representation.MethodDescriptor; 15import cuchaz.enigma.translation.representation.MethodDescriptor;
16import cuchaz.enigma.translation.representation.ReferencedEntryPool;
17import cuchaz.enigma.translation.representation.TypeDescriptor; 16import cuchaz.enigma.translation.representation.TypeDescriptor;
18import cuchaz.enigma.translation.representation.entry.*; 17import cuchaz.enigma.translation.representation.entry.*;
19import org.objectweb.asm.*; 18import org.objectweb.asm.*;
@@ -22,14 +21,12 @@ import java.util.Arrays;
22 21
23public class TranslationClassVisitor extends ClassVisitor { 22public class TranslationClassVisitor extends ClassVisitor {
24 private final Translator translator; 23 private final Translator translator;
25 private final ReferencedEntryPool entryPool;
26 24
27 private ClassDefEntry obfClassEntry; 25 private ClassDefEntry obfClassEntry;
28 26
29 public TranslationClassVisitor(Translator translator, ReferencedEntryPool entryPool, int api, ClassVisitor cv) { 27 public TranslationClassVisitor(Translator translator, int api, ClassVisitor cv) {
30 super(api, cv); 28 super(api, cv);
31 this.translator = translator; 29 this.translator = translator;
32 this.entryPool = entryPool;
33 } 30 }
34 31
35 @Override 32 @Override
@@ -57,7 +54,7 @@ public class TranslationClassVisitor extends ClassVisitor {
57 MethodDefEntry translatedEntry = translator.translate(entry); 54 MethodDefEntry translatedEntry = translator.translate(entry);
58 String[] translatedExceptions = new String[exceptions.length]; 55 String[] translatedExceptions = new String[exceptions.length];
59 for (int i = 0; i < exceptions.length; i++) { 56 for (int i = 0; i < exceptions.length; i++) {
60 translatedExceptions[i] = translator.translate(entryPool.getClass(exceptions[i])).getFullName(); 57 translatedExceptions[i] = translator.translate(new ClassEntry(exceptions[i])).getFullName();
61 } 58 }
62 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); 59 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions);
63 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); 60 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv);
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
index a5a33e69..c824265f 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -1,16 +1,14 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.translation.LocalNameGenerator;
3import cuchaz.enigma.translation.Translator; 4import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.NameValidator;
5import cuchaz.enigma.translation.representation.MethodDescriptor; 5import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.Signature; 6import cuchaz.enigma.translation.representation.Signature;
7import cuchaz.enigma.translation.representation.TypeDescriptor; 7import cuchaz.enigma.translation.representation.TypeDescriptor;
8import cuchaz.enigma.translation.representation.entry.*; 8import cuchaz.enigma.translation.representation.entry.*;
9import org.objectweb.asm.*; 9import org.objectweb.asm.*;
10 10
11import java.util.Collection;
12import java.util.List; 11import java.util.List;
13import java.util.Locale;
14import java.util.stream.Collectors; 12import java.util.stream.Collectors;
15 13
16public class TranslationMethodVisitor extends MethodVisitor { 14public class TranslationMethodVisitor extends MethodVisitor {
@@ -84,40 +82,6 @@ public class TranslationMethodVisitor extends MethodVisitor {
84 } 82 }
85 83
86 @Override 84 @Override
87 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
88 hasParameterMeta = true;
89
90 String translatedSignature = translator.translate(Signature.createTypedSignature(signature)).toString();
91 int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index);
92
93 if (argumentIndex >= 0) {
94 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, true, new TypeDescriptor(desc));
95 LocalVariableDefEntry translatedEntry = translator.translate(entry);
96 String translatedName = translatedEntry.getName();
97
98 if (translatedName.equals(entry.getName())) {
99 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
100 List<TypeDescriptor> translatedArguments = arguments.stream()
101 .map(translator::translate)
102 .collect(Collectors.toList());
103
104 boolean argument = argumentIndex < arguments.size();
105 if (argument) {
106 translatedName = inferArgumentName(argumentIndex, translatedEntry.getDesc(), translatedArguments);
107 } else {
108 translatedName = inferLocalVariableName(argumentIndex, translatedEntry.getDesc());
109 }
110 }
111
112 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index);
113 } else {
114 // Handle "this" variable
115 TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc));
116 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index);
117 }
118 }
119
120 @Override
121 public void visitTypeInsn(int opcode, String type) { 85 public void visitTypeInsn(int opcode, String type) {
122 ClassEntry translatedEntry = translator.translate(new ClassEntry(type)); 86 ClassEntry translatedEntry = translator.translate(new ClassEntry(type));
123 super.visitTypeInsn(opcode, translatedEntry.getFullName()); 87 super.visitTypeInsn(opcode, translatedEntry.getFullName());
@@ -154,10 +118,44 @@ public class TranslationMethodVisitor extends MethodVisitor {
154 } 118 }
155 119
156 @Override 120 @Override
121 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
122 hasParameterMeta = true;
123
124 String translatedSignature = translator.translate(Signature.createTypedSignature(signature)).toString();
125 int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index);
126
127 if (argumentIndex >= 0) {
128 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, true, new TypeDescriptor(desc));
129 LocalVariableDefEntry translatedEntry = translator.translate(entry);
130 String translatedName = translatedEntry.getName();
131
132 if (translatedName.equals(entry.getName())) {
133 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
134 List<TypeDescriptor> translatedArguments = arguments.stream()
135 .map(translator::translate)
136 .collect(Collectors.toList());
137
138 boolean argument = argumentIndex < arguments.size();
139 if (argument) {
140 translatedName = LocalNameGenerator.generateArgumentName(argumentIndex, translatedEntry.getDesc(), translatedArguments);
141 } else {
142 translatedName = LocalNameGenerator.generateLocalVariableName(argumentIndex, translatedEntry.getDesc());
143 }
144 }
145
146 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index);
147 } else {
148 // Handle "this" variable
149 TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc));
150 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index);
151 }
152 }
153
154 @Override
157 public void visitEnd() { 155 public void visitEnd() {
158 // If we didn't receive any parameter metadata, generate it 156 // If we didn't receive any parameter metadata, generate it
159 if (!hasParameterMeta) { 157 if (!hasParameterMeta) {
160 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); 158 List<TypeDescriptor> arguments = translator.translate(methodEntry.getDesc()).getArgumentDescs();
161 int offset = ((methodEntry.getAccess().getFlags() & Opcodes.ACC_ABSTRACT) != 0) ? 1 : 0; 159 int offset = ((methodEntry.getAccess().getFlags() & Opcodes.ACC_ABSTRACT) != 0) ? 1 : 0;
162 160
163 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { 161 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) {
@@ -165,7 +163,7 @@ public class TranslationMethodVisitor extends MethodVisitor {
165 LocalVariableEntry translatedEntry = translator.translate(entry); 163 LocalVariableEntry translatedEntry = translator.translate(entry);
166 String translatedName = translatedEntry.getName(); 164 String translatedName = translatedEntry.getName();
167 if (translatedName.equals(entry.getName())) { 165 if (translatedName.equals(entry.getName())) {
168 super.visitParameter(inferArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0); 166 super.visitParameter(LocalNameGenerator.generateArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0);
169 } else { 167 } else {
170 super.visitParameter(translatedName, 0); 168 super.visitParameter(translatedName, 0);
171 } 169 }
@@ -175,39 +173,4 @@ public class TranslationMethodVisitor extends MethodVisitor {
175 } 173 }
176 super.visitEnd(); 174 super.visitEnd();
177 } 175 }
178
179 private String inferArgumentName(int index, TypeDescriptor desc, Collection<TypeDescriptor> arguments) {
180 boolean uniqueType = arguments.stream().filter(desc::equals).count() <= 1;
181 String translatedName;
182 int nameIndex = index + 1;
183 StringBuilder nameBuilder = new StringBuilder(getTypeName(desc));
184 if (!uniqueType || NameValidator.isReserved(nameBuilder.toString())) {
185 nameBuilder.append(nameIndex);
186 }
187 translatedName = nameBuilder.toString();
188 return translatedName;
189 }
190
191 private String inferLocalVariableName(int index, TypeDescriptor desc) {
192 int nameIndex = index + 1;
193 return getTypeName(desc) + nameIndex;
194 }
195
196 private String getTypeName(TypeDescriptor desc) {
197 // Unfortunately each of these have different name getters, so they have different code paths
198 if (desc.isPrimitive()) {
199 TypeDescriptor.Primitive argCls = desc.getPrimitive();
200 return argCls.name().toLowerCase(Locale.ROOT);
201 } else if (desc.isArray()) {
202 // List types would require this whole block again, so just go with aListx
203 return "arr";
204 } else if (desc.isType()) {
205 String typeName = desc.getTypeEntry().getSimpleName().replace("$", "");
206 typeName = typeName.substring(0, 1).toLowerCase(Locale.ROOT) + typeName.substring(1);
207 return typeName;
208 } else {
209 System.err.println("Encountered invalid argument type descriptor " + desc.toString());
210 return "var";
211 }
212 }
213} 176}
diff --git a/src/main/java/cuchaz/enigma/config/Config.java b/src/main/java/cuchaz/enigma/config/Config.java
index 55f867ee..6f5a337c 100644
--- a/src/main/java/cuchaz/enigma/config/Config.java
+++ b/src/main/java/cuchaz/enigma/config/Config.java
@@ -74,7 +74,6 @@ public class Config {
74 config.proposedColorOutline = new AlphaColorEntry(0x000000, 0.15f); 74 config.proposedColorOutline = new AlphaColorEntry(0x000000, 0.15f);
75 config.deobfuscatedColor = new AlphaColorEntry(0xDCFFDC, 1.0f); 75 config.deobfuscatedColor = new AlphaColorEntry(0xDCFFDC, 1.0f);
76 config.deobfuscatedColorOutline = new AlphaColorEntry(0x50A050, 1.0f); 76 config.deobfuscatedColorOutline = new AlphaColorEntry(0x50A050, 1.0f);
77 config.otherColorOutline = new AlphaColorEntry(0xB4B4B4, 1.0f);
78 config.editorBackground = 0xFFFFFF; 77 config.editorBackground = 0xFFFFFF;
79 config.highlightColor = 0x3333EE; 78 config.highlightColor = 0x3333EE;
80 config.caretColor = 0x000000; 79 config.caretColor = 0x000000;
@@ -98,7 +97,6 @@ public class Config {
98 config.deobfuscatedColorOutline = new AlphaColorEntry(0x50FA7B, 0.5f); 97 config.deobfuscatedColorOutline = new AlphaColorEntry(0x50FA7B, 0.5f);
99 config.proposedColor = new AlphaColorEntry(0x606366, 0.3f); 98 config.proposedColor = new AlphaColorEntry(0x606366, 0.3f);
100 config.proposedColorOutline = new AlphaColorEntry(0x606366, 0.5f); 99 config.proposedColorOutline = new AlphaColorEntry(0x606366, 0.5f);
101 config.otherColorOutline = new AlphaColorEntry(0xB4B4B4, 0.0f);
102 config.editorBackground = 0x282A36; 100 config.editorBackground = 0x282A36;
103 config.highlightColor = 0xFF79C6; 101 config.highlightColor = 0xFF79C6;
104 config.caretColor = 0xF8F8F2; 102 config.caretColor = 0xF8F8F2;
@@ -128,7 +126,6 @@ public class Config {
128 public AlphaColorEntry proposedColorOutline; 126 public AlphaColorEntry proposedColorOutline;
129 public AlphaColorEntry deobfuscatedColor; 127 public AlphaColorEntry deobfuscatedColor;
130 public AlphaColorEntry deobfuscatedColorOutline; 128 public AlphaColorEntry deobfuscatedColorOutline;
131 public AlphaColorEntry otherColorOutline;
132 129
133 public Integer editorBackground; 130 public Integer editorBackground;
134 public Integer highlightColor; 131 public Integer highlightColor;
@@ -198,12 +195,14 @@ public class Config {
198 } 195 }
199 196
200 private static class IntSerializer implements JsonSerializer<Integer> { 197 private static class IntSerializer implements JsonSerializer<Integer> {
198 @Override
201 public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { 199 public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
202 return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase()); 200 return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase());
203 } 201 }
204 } 202 }
205 203
206 private static class IntDeserializer implements JsonDeserializer<Integer> { 204 private static class IntDeserializer implements JsonDeserializer<Integer> {
205 @Override
207 public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { 206 public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
208 return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); 207 return (int) Long.parseLong(json.getAsString().replace("#", ""), 16);
209 } 208 }
diff --git a/src/main/java/cuchaz/enigma/config/Themes.java b/src/main/java/cuchaz/enigma/config/Themes.java
index 00324f47..400ea3f0 100644
--- a/src/main/java/cuchaz/enigma/config/Themes.java
+++ b/src/main/java/cuchaz/enigma/config/Themes.java
@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap;
4import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
5import cuchaz.enigma.gui.EnigmaSyntaxKit; 5import cuchaz.enigma.gui.EnigmaSyntaxKit;
6import cuchaz.enigma.gui.highlight.BoxHighlightPainter; 6import cuchaz.enigma.gui.highlight.BoxHighlightPainter;
7import cuchaz.enigma.gui.highlight.TokenHighlightType;
7import de.sciss.syntaxpane.DefaultSyntaxKit; 8import de.sciss.syntaxpane.DefaultSyntaxKit;
8 9
9import javax.swing.*; 10import javax.swing.*;
@@ -17,10 +18,11 @@ public class Themes {
17 } 18 }
18 19
19 public static void updateTheme(Gui gui) { 20 public static void updateTheme(Gui gui) {
20 Config.getInstance().lookAndFeel.apply(Config.getInstance()); 21 Config config = Config.getInstance();
21 Config.getInstance().lookAndFeel.setGlobalLAF(); 22 config.lookAndFeel.apply(config);
23 config.lookAndFeel.setGlobalLAF();
22 try { 24 try {
23 Config.getInstance().saveConfig(); 25 config.saveConfig();
24 } catch (IOException e) { 26 } catch (IOException e) {
25 e.printStackTrace(); 27 e.printStackTrace();
26 } 28 }
@@ -28,12 +30,11 @@ public class Themes {
28 DefaultSyntaxKit.initKit(); 30 DefaultSyntaxKit.initKit();
29 DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName()); 31 DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName());
30 gui.boxHighlightPainters = ImmutableMap.of( 32 gui.boxHighlightPainters = ImmutableMap.of(
31 "obfuscated", BoxHighlightPainter.create(Config.getInstance().obfuscatedColor, Config.getInstance().obfuscatedColorOutline), 33 TokenHighlightType.OBFUSCATED, BoxHighlightPainter.create(config.obfuscatedColor, config.obfuscatedColorOutline),
32 "proposed", BoxHighlightPainter.create(Config.getInstance().proposedColor, Config.getInstance().proposedColorOutline), 34 TokenHighlightType.PROPOSED, BoxHighlightPainter.create(config.proposedColor, config.proposedColorOutline),
33 "deobfuscated", BoxHighlightPainter.create(Config.getInstance().deobfuscatedColor, Config.getInstance().deobfuscatedColorOutline), 35 TokenHighlightType.DEOBFUSCATED, BoxHighlightPainter.create(config.deobfuscatedColor, config.deobfuscatedColorOutline)
34 "other", BoxHighlightPainter.create(null, Config.getInstance().otherColorOutline)
35 ); 36 );
36 gui.setEditorTheme(Config.getInstance().lookAndFeel); 37 gui.setEditorTheme(config.lookAndFeel);
37 SwingUtilities.updateComponentTreeUI(gui.getFrame()); 38 SwingUtilities.updateComponentTreeUI(gui.getFrame());
38 } 39 }
39} 40}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index c3b7288c..39d0333b 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -17,9 +17,11 @@ 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.translation.representation.entry.ClassEntry;
21import cuchaz.enigma.throwables.IllegalNameException; 20import cuchaz.enigma.throwables.IllegalNameException;
21import cuchaz.enigma.translation.Translator;
22import cuchaz.enigma.translation.representation.entry.ClassEntry;
22 23
24import javax.annotation.Nullable;
23import javax.swing.*; 25import javax.swing.*;
24import javax.swing.event.CellEditorListener; 26import javax.swing.event.CellEditorListener;
25import javax.swing.event.ChangeEvent; 27import javax.swing.event.ChangeEvent;
@@ -27,21 +29,26 @@ import javax.swing.tree.*;
27import java.awt.event.MouseAdapter; 29import java.awt.event.MouseAdapter;
28import java.awt.event.MouseEvent; 30import java.awt.event.MouseEvent;
29import java.util.*; 31import java.util.*;
30import java.util.List;
31 32
32public class ClassSelector extends JTree { 33public class ClassSelector extends JTree {
33 34
34 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); 35 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName);
36
37 private final GuiController controller;
38
35 private DefaultMutableTreeNode rootNodes; 39 private DefaultMutableTreeNode rootNodes;
36 private ClassSelectionListener selectionListener; 40 private ClassSelectionListener selectionListener;
37 private RenameSelectionListener renameSelectionListener; 41 private RenameSelectionListener renameSelectionListener;
38 private Comparator<ClassEntry> comparator; 42 private Comparator<ClassEntry> comparator;
39 43
44 private final Map<ClassEntry, ClassEntry> displayedObfToDeobf = new HashMap<>();
45
40 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { 46 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) {
41 this.comparator = comparator; 47 this.comparator = comparator;
48 this.controller = gui.getController();
42 49
43 // configure the tree control 50 // configure the tree control
44 setEditable(gui != null); 51 setEditable(true);
45 setRootVisible(false); 52 setRootVisible(false);
46 setShowsRootHandles(false); 53 setShowsRootHandles(false);
47 setModel(null); 54 setModel(null);
@@ -55,66 +62,64 @@ public class ClassSelector extends JTree {
55 TreePath path = getSelectionPath(); 62 TreePath path = getSelectionPath();
56 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { 63 if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) {
57 ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); 64 ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent();
58 selectionListener.onSelectClass(node.getClassEntry()); 65 selectionListener.onSelectClass(node.getObfEntry());
59 } 66 }
60 } 67 }
61 } 68 }
62 }); 69 });
63 70
64 if (gui != null) { 71 final JTree tree = this;
65 final JTree tree = this;
66 72
67 final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, 73 final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree,
68 (DefaultTreeCellRenderer) tree.getCellRenderer()) { 74 (DefaultTreeCellRenderer) tree.getCellRenderer()) {
69 @Override 75 @Override
70 public boolean isCellEditable(EventObject event) { 76 public boolean isCellEditable(EventObject event) {
71 return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); 77 return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event);
72 } 78 }
73 }; 79 };
74 this.setCellEditor(editor); 80 this.setCellEditor(editor);
75 editor.addCellEditorListener(new CellEditorListener() { 81 editor.addCellEditorListener(new CellEditorListener() {
76 @Override 82 @Override
77 public void editingStopped(ChangeEvent e) { 83 public void editingStopped(ChangeEvent e) {
78 String data = editor.getCellEditorValue().toString(); 84 String data = editor.getCellEditorValue().toString();
79 TreePath path = getSelectionPath(); 85 TreePath path = getSelectionPath();
80 86
81 Object realPath = path.getLastPathComponent(); 87 Object realPath = path.getLastPathComponent();
82 if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { 88 if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) {
83 DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; 89 DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath;
84 TreeNode parentNode = node.getParent(); 90 TreeNode parentNode = node.getParent();
85 if (parentNode == null) 91 if (parentNode == null)
86 return; 92 return;
87 boolean allowEdit = true; 93 boolean allowEdit = true;
88 for (int i = 0; i < parentNode.getChildCount(); i++) { 94 for (int i = 0; i < parentNode.getChildCount(); i++) {
89 TreeNode childNode = parentNode.getChildAt(i); 95 TreeNode childNode = parentNode.getChildAt(i);
90 if (childNode != null && childNode.toString().equals(data) && childNode != node) { 96 if (childNode != null && childNode.toString().equals(data) && childNode != node) {
91 allowEdit = false; 97 allowEdit = false;
92 break; 98 break;
93 }
94 } 99 }
95 if (allowEdit && renameSelectionListener != null) {
96 Object prevData = node.getUserObject();
97 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data;
98 try {
99 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node);
100 node.setUserObject(objectData); // Make sure that it's modified
101 } catch (IllegalNameException ex) {
102 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION,
103 JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK");
104 editor.cancelCellEditing();
105 }
106 } else
107 editor.cancelCellEditing();
108 } 100 }
109 101 if (allowEdit && renameSelectionListener != null) {
102 Object prevData = node.getUserObject();
103 Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data;
104 try {
105 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node);
106 node.setUserObject(objectData); // Make sure that it's modified
107 } catch (IllegalNameException ex) {
108 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION,
109 JOptionPane.ERROR_MESSAGE, null, new String[]{"Ok"}, "OK");
110 editor.cancelCellEditing();
111 }
112 } else
113 editor.cancelCellEditing();
110 } 114 }
111 115
112 @Override 116 }
113 public void editingCanceled(ChangeEvent e) { 117
114 // NOP 118 @Override
115 } 119 public void editingCanceled(ChangeEvent e) {
116 }); 120 // NOP
117 } 121 }
122 });
118 // init defaults 123 // init defaults
119 this.selectionListener = null; 124 this.selectionListener = null;
120 this.renameSelectionListener = null; 125 this.renameSelectionListener = null;
@@ -142,16 +147,21 @@ public class ClassSelector extends JTree {
142 } 147 }
143 148
144 public void setClasses(Collection<ClassEntry> classEntries) { 149 public void setClasses(Collection<ClassEntry> classEntries) {
150 displayedObfToDeobf.clear();
151
145 List<StateEntry> state = getExpansionState(this); 152 List<StateEntry> state = getExpansionState(this);
146 if (classEntries == null) { 153 if (classEntries == null) {
147 setModel(null); 154 setModel(null);
148 return; 155 return;
149 } 156 }
150 157
158 Translator translator = controller.getDeobfuscator().getMapper().getDeobfuscator();
159
151 // build the package names 160 // build the package names
152 Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); 161 Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap();
153 for (ClassEntry classEntry : classEntries) { 162 for (ClassEntry obfClass : classEntries) {
154 packages.put(classEntry.getPackageName(), null); 163 ClassEntry deobfClass = translator.translate(obfClass);
164 packages.put(deobfClass.getPackageName(), null);
155 } 165 }
156 166
157 // sort the packages 167 // sort the packages
@@ -191,20 +201,24 @@ public class ClassSelector extends JTree {
191 201
192 // put the classes into packages 202 // put the classes into packages
193 Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); 203 Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create();
194 for (ClassEntry classEntry : classEntries) { 204 for (ClassEntry obfClass : classEntries) {
195 packagedClassEntries.put(classEntry.getPackageName(), classEntry); 205 ClassEntry deobfClass = translator.translate(obfClass);
206 packagedClassEntries.put(deobfClass.getPackageName(), obfClass);
196 } 207 }
197 208
198 // build the class nodes 209 // build the class nodes
199 for (String packageName : packagedClassEntries.keySet()) { 210 for (String packageName : packagedClassEntries.keySet()) {
200 // sort the class entries 211 // sort the class entries
201 List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); 212 List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName));
202 classEntriesInPackage.sort(this.comparator); 213 classEntriesInPackage.sort((o1, o2) -> comparator.compare(translator.translate(o1), translator.translate(o2)));
203 214
204 // create the nodes in order 215 // create the nodes in order
205 for (ClassEntry classEntry : classEntriesInPackage) { 216 for (ClassEntry obfClass : classEntriesInPackage) {
217 ClassEntry deobfClass = translator.translate(obfClass);
206 ClassSelectorPackageNode node = packages.get(packageName); 218 ClassSelectorPackageNode node = packages.get(packageName);
207 node.add(new ClassSelectorClassNode(classEntry)); 219 ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfClass, deobfClass);
220 displayedObfToDeobf.put(obfClass, deobfClass);
221 node.add(classNode);
208 } 222 }
209 } 223 }
210 224
@@ -324,7 +338,7 @@ public class ClassSelector extends JTree {
324 } 338 }
325 for (ClassSelectorPackageNode packageNode : packageNodes()) { 339 for (ClassSelectorPackageNode packageNode : packageNodes()) {
326 if (packageNode.getPackageName().equals(packageName)) { 340 if (packageNode.getPackageName().equals(packageName)) {
327 expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); 341 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode}));
328 return; 342 return;
329 } 343 }
330 } 344 }
@@ -332,14 +346,13 @@ public class ClassSelector extends JTree {
332 346
333 public void expandAll() { 347 public void expandAll() {
334 for (ClassSelectorPackageNode packageNode : packageNodes()) { 348 for (ClassSelectorPackageNode packageNode : packageNodes()) {
335 expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); 349 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode}));
336 } 350 }
337 } 351 }
338 352
339 public ClassEntry getFirstClass() { 353 public ClassEntry getFirstClass() {
340 ClassSelectorPackageNode packageNode = packageNodes().get(0); 354 ClassSelectorPackageNode packageNode = packageNodes().get(0);
341 if (packageNode != null) 355 if (packageNode != null) {
342 {
343 ClassSelectorClassNode classNode = classNodes(packageNode).get(0); 356 ClassSelectorClassNode classNode = classNodes(packageNode).get(0);
344 if (classNode != null) { 357 if (classNode != null) {
345 return classNode.getClassEntry(); 358 return classNode.getClassEntry();
@@ -350,7 +363,7 @@ public class ClassSelector extends JTree {
350 363
351 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { 364 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) {
352 String packageName = entry.getPackageName(); 365 String packageName = entry.getPackageName();
353 if (packageName == null){ 366 if (packageName == null) {
354 packageName = "(none)"; 367 packageName = "(none)";
355 } 368 }
356 for (ClassSelectorPackageNode packageNode : packageNodes()) { 369 for (ClassSelectorPackageNode packageNode : packageNodes()) {
@@ -361,6 +374,11 @@ public class ClassSelector extends JTree {
361 return null; 374 return null;
362 } 375 }
363 376
377 @Nullable
378 public ClassEntry getDisplayedDeobf(ClassEntry obfEntry) {
379 return displayedObfToDeobf.get(obfEntry);
380 }
381
364 public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { 382 public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) {
365 ClassSelectorPackageNode packageNode = getPackageNode(entry); 383 ClassSelectorPackageNode packageNode = getPackageNode(entry);
366 384
@@ -402,7 +420,7 @@ public class ClassSelector extends JTree {
402 for (ClassSelectorPackageNode packageNode : packageNodes()) { 420 for (ClassSelectorPackageNode packageNode : packageNodes()) {
403 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 421 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
404 if (classNode.getClassEntry().equals(classEntry)) { 422 if (classNode.getClassEntry().equals(classEntry)) {
405 setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode })); 423 setSelectionPath(new TreePath(new Object[]{getModel().getRoot(), packageNode, classNode}));
406 } 424 }
407 } 425 }
408 } 426 }
@@ -418,6 +436,9 @@ public class ClassSelector extends JTree {
418 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); 436 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i);
419 if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { 437 if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) {
420 model.removeNodeFromParent(childNode); 438 model.removeNodeFromParent(childNode);
439 if (childNode instanceof ClassSelectorClassNode) {
440 displayedObfToDeobf.remove(((ClassSelectorClassNode) childNode).getObfEntry());
441 }
421 break; 442 break;
422 } 443 }
423 } 444 }
@@ -428,13 +449,25 @@ public class ClassSelector extends JTree {
428 ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); 449 ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode);
429 } 450 }
430 451
431 public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) { 452 public void moveClassIn(ClassEntry classEntry) {
432 if (otherSelector == null) 453 removeEntry(classEntry);
433 removeNode(getPackageNode(oldClassEntry), oldClassEntry); 454 insertNode(classEntry);
434 insertNode(getOrCreate(newClassEntry), newClassEntry);
435 } 455 }
436 456
437 public ClassSelectorPackageNode getOrCreate(ClassEntry entry) { 457 public void moveClassOut(ClassEntry classEntry) {
458 removeEntry(classEntry);
459 }
460
461 private void removeEntry(ClassEntry classEntry) {
462 ClassEntry previousDeobf = displayedObfToDeobf.get(classEntry);
463 if (previousDeobf != null) {
464 ClassSelectorPackageNode packageNode = getPackageNode(previousDeobf);
465 removeNode(packageNode, previousDeobf);
466 removeNodeIfEmpty(packageNode);
467 }
468 }
469
470 public ClassSelectorPackageNode getOrCreatePackage(ClassEntry entry) {
438 DefaultTreeModel model = (DefaultTreeModel) getModel(); 471 DefaultTreeModel model = (DefaultTreeModel) getModel();
439 ClassSelectorPackageNode newPackageNode = getPackageNode(entry); 472 ClassSelectorPackageNode newPackageNode = getPackageNode(entry);
440 if (newPackageNode == null) { 473 if (newPackageNode == null) {
@@ -444,10 +477,15 @@ public class ClassSelector extends JTree {
444 return newPackageNode; 477 return newPackageNode;
445 } 478 }
446 479
447 public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { 480 public void insertNode(ClassEntry obfEntry) {
481 ClassEntry deobfEntry = controller.getDeobfuscator().deobfuscate(obfEntry);
482 ClassSelectorPackageNode packageNode = getOrCreatePackage(deobfEntry);
483
448 DefaultTreeModel model = (DefaultTreeModel) getModel(); 484 DefaultTreeModel model = (DefaultTreeModel) getModel();
449 ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); 485 ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfEntry, deobfEntry);
450 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); 486 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode));
487
488 displayedObfToDeobf.put(obfEntry, deobfEntry);
451 } 489 }
452 490
453 public void reload() { 491 public void reload() {
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
index 08100438..e119640a 100644
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java
@@ -11,58 +11,27 @@
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import com.strobel.decompiler.languages.java.ast.CompilationUnit;
15import cuchaz.enigma.Deobfuscator;
16import cuchaz.enigma.analysis.EntryReference;
17import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.analysis.Token; 14import cuchaz.enigma.analysis.Token;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.Entry;
21import de.sciss.syntaxpane.DefaultSyntaxKit;
22 15
23import javax.swing.*; 16import javax.swing.*;
24import javax.swing.text.BadLocationException; 17import javax.swing.text.BadLocationException;
18import javax.swing.text.Document;
25import javax.swing.text.Highlighter.HighlightPainter; 19import javax.swing.text.Highlighter.HighlightPainter;
26import java.awt.*; 20import java.awt.*;
27import java.awt.event.ActionEvent; 21import java.awt.event.ActionEvent;
28import java.awt.event.ActionListener; 22import java.awt.event.ActionListener;
29 23
30public class CodeReader extends JEditorPane { 24public class CodeReader extends JEditorPane {
31
32 private static final long serialVersionUID = 3673180950485748810L; 25 private static final long serialVersionUID = 3673180950485748810L;
33 26
34 private static final Object lock = new Object();
35 private SourceIndex sourceIndex;
36 private SelectionListener selectionListener;
37
38 public CodeReader() {
39
40 setEditable(false);
41 setContentType("text/java");
42
43 // turn off token highlighting (it's wrong most of the time anyway...)
44 DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit();
45 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker");
46
47 // hook events
48 addCaretListener(event ->
49 {
50 if (selectionListener != null && sourceIndex != null) {
51 Token token = sourceIndex.getReferenceToken(event.getDot());
52 if (token != null) {
53 selectionListener.onSelect(sourceIndex.getDeobfReference(token));
54 } else {
55 selectionListener.onSelect(null);
56 }
57 }
58 });
59 }
60
61 // HACKHACK: someday we can update the main GUI to use this code reader 27 // HACKHACK: someday we can update the main GUI to use this code reader
62 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { 28 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) {
63 29
64 // set the caret position to the token 30 // set the caret position to the token
65 editor.setCaretPosition(token.start); 31 Document document = editor.getDocument();
32 int clampedPosition = Math.min(Math.max(token.start, 0), document.getLength());
33
34 editor.setCaretPosition(clampedPosition);
66 editor.grabFocus(); 35 editor.grabFocus();
67 36
68 try { 37 try {
@@ -101,57 +70,4 @@ public class CodeReader extends JEditorPane {
101 }); 70 });
102 timer.start(); 71 timer.start();
103 } 72 }
104
105 public void setSelectionListener(SelectionListener val) {
106 selectionListener = val;
107 }
108
109 public void setCode(String code) {
110 // sadly, the java lexer is not thread safe, so we have to serialize all these calls
111 synchronized (lock) {
112 setText(code);
113 }
114 }
115
116 public SourceIndex getSourceIndex() {
117 return sourceIndex;
118 }
119
120 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) {
121 decompileClass(classEntry, deobfuscator, null);
122 }
123
124 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) {
125 decompileClass(classEntry, deobfuscator, null, callback);
126 }
127
128 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) {
129
130 if (classEntry == null) {
131 setCode(null);
132 return;
133 }
134
135 setCode("(decompiling...)");
136
137 // run decompilation in a separate thread to keep ui responsive
138 new Thread(() ->
139 {
140
141 // decompile it
142
143 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getName());
144 String source = deobfuscator.getSource(sourceTree);
145 setCode(source);
146 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
147
148 if (callback != null) {
149 callback.run();
150 }
151 }).start();
152 }
153
154 public interface SelectionListener {
155 void onSelect(EntryReference<Entry<?>, Entry<?>> reference);
156 }
157} 73}
diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
new file mode 100644
index 00000000..03f76c90
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java
@@ -0,0 +1,129 @@
1package cuchaz.enigma.gui;
2
3import cuchaz.enigma.Deobfuscator;
4import cuchaz.enigma.analysis.EntryReference;
5import cuchaz.enigma.analysis.SourceIndex;
6import cuchaz.enigma.analysis.Token;
7import cuchaz.enigma.api.EnigmaPlugin;
8import cuchaz.enigma.gui.highlight.TokenHighlightType;
9import cuchaz.enigma.translation.LocalNameGenerator;
10import cuchaz.enigma.translation.Translator;
11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.Entry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry;
16
17import javax.annotation.Nullable;
18import java.util.*;
19
20public class DecompiledClassSource {
21 private final ClassEntry classEntry;
22 private final Deobfuscator deobfuscator;
23
24 private final SourceIndex obfuscatedIndex;
25 private SourceIndex remappedIndex;
26
27 private final Map<TokenHighlightType, Collection<Token>> highlightedTokens = new EnumMap<>(TokenHighlightType.class);
28
29 public DecompiledClassSource(ClassEntry classEntry, Deobfuscator deobfuscator, SourceIndex index) {
30 this.classEntry = classEntry;
31 this.deobfuscator = deobfuscator;
32 this.obfuscatedIndex = index;
33 this.remappedIndex = index;
34 }
35
36 public void remapSource(Translator translator) {
37 highlightedTokens.clear();
38
39 SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens());
40
41 SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(token, movedToken, translator));
42 remappedIndex = obfuscatedIndex.remapTo(remapResult);
43 }
44
45 private String remapToken(Token token, Token movedToken, Translator translator) {
46 EntryReference<Entry<?>, Entry<?>> reference = obfuscatedIndex.getReference(token);
47
48 if (deobfuscator.isRenamable(reference)) {
49 Entry<?> entry = reference.getNameableEntry();
50 Entry<?> translatedEntry = translator.translate(entry);
51
52 if (isDeobfuscated(entry, translatedEntry)) {
53 highlightToken(movedToken, TokenHighlightType.DEOBFUSCATED);
54 return translatedEntry.getSourceRemapName();
55 } else {
56 String proposedName = proposeName(entry);
57 if (proposedName != null) {
58 highlightToken(movedToken, TokenHighlightType.PROPOSED);
59 return proposedName;
60 }
61
62 highlightToken(movedToken, TokenHighlightType.OBFUSCATED);
63
64 String defaultName = generateDefaultName(translatedEntry);
65 if (defaultName != null) {
66 return defaultName;
67 }
68 }
69 }
70
71 return null;
72 }
73
74 @Nullable
75 private String proposeName(Entry<?> entry) {
76 if (entry instanceof FieldEntry) {
77 for (EnigmaPlugin plugin : deobfuscator.getPlugins()) {
78 String owner = entry.getContainingClass().getFullName();
79 String proposal = plugin.proposeFieldName(owner, entry.getName(), ((FieldEntry) entry).getDesc().toString());
80 if (proposal != null) {
81 return proposal;
82 }
83 }
84 }
85 return null;
86 }
87
88 @Nullable
89 private String generateDefaultName(Entry<?> entry) {
90 if (entry instanceof LocalVariableDefEntry) {
91 LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry;
92
93 int index = localVariable.getIndex();
94 if (localVariable.isArgument()) {
95 List<TypeDescriptor> arguments = localVariable.getParent().getDesc().getArgumentDescs();
96 return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments);
97 } else {
98 return LocalNameGenerator.generateLocalVariableName(index, localVariable.getDesc());
99 }
100 }
101
102 return null;
103 }
104
105 private boolean isDeobfuscated(Entry<?> entry, Entry<?> translatedEntry) {
106 return !entry.getName().equals(translatedEntry.getName());
107 }
108
109 public ClassEntry getEntry() {
110 return classEntry;
111 }
112
113 public SourceIndex getIndex() {
114 return remappedIndex;
115 }
116
117 public Map<TokenHighlightType, Collection<Token>> getHighlightedTokens() {
118 return highlightedTokens;
119 }
120
121 private void highlightToken(Token token, TokenHighlightType highlightType) {
122 highlightedTokens.computeIfAbsent(highlightType, t -> new ArrayList<>()).add(token);
123 }
124
125 @Override
126 public String toString() {
127 return remappedIndex.getSource();
128 }
129}
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index d119735c..a6e20a27 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -24,7 +24,7 @@ import cuchaz.enigma.gui.filechooser.FileChooserAny;
24import cuchaz.enigma.gui.filechooser.FileChooserFolder; 24import cuchaz.enigma.gui.filechooser.FileChooserFolder;
25import cuchaz.enigma.gui.highlight.BoxHighlightPainter; 25import cuchaz.enigma.gui.highlight.BoxHighlightPainter;
26import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; 26import cuchaz.enigma.gui.highlight.SelectionHighlightPainter;
27import cuchaz.enigma.gui.node.ClassSelectorPackageNode; 27import cuchaz.enigma.gui.highlight.TokenHighlightType;
28import cuchaz.enigma.gui.panels.PanelDeobf; 28import cuchaz.enigma.gui.panels.PanelDeobf;
29import cuchaz.enigma.gui.panels.PanelEditor; 29import cuchaz.enigma.gui.panels.PanelEditor;
30import cuchaz.enigma.gui.panels.PanelIdentifier; 30import cuchaz.enigma.gui.panels.PanelIdentifier;
@@ -44,10 +44,9 @@ import javax.swing.tree.TreeNode;
44import javax.swing.tree.TreePath; 44import javax.swing.tree.TreePath;
45import java.awt.*; 45import java.awt.*;
46import java.awt.event.*; 46import java.awt.event.*;
47import java.io.IOException;
48import java.nio.file.Path; 47import java.nio.file.Path;
49import java.util.*;
50import java.util.List; 48import java.util.List;
49import java.util.*;
51import java.util.function.Function; 50import java.util.function.Function;
52 51
53public class Gui { 52public class Gui {
@@ -71,7 +70,7 @@ public class Gui {
71 private JPanel classesPanel; 70 private JPanel classesPanel;
72 private JSplitPane splitClasses; 71 private JSplitPane splitClasses;
73 private PanelIdentifier infoPanel; 72 private PanelIdentifier infoPanel;
74 public Map<String, BoxHighlightPainter> boxHighlightPainters; 73 public Map<TokenHighlightType, BoxHighlightPainter> boxHighlightPainters;
75 private SelectionHighlightPainter selectionHighlightPainter; 74 private SelectionHighlightPainter selectionHighlightPainter;
76 private JTree inheritanceTree; 75 private JTree inheritanceTree;
77 private JTree implementationsTree; 76 private JTree implementationsTree;
@@ -320,7 +319,7 @@ public class Gui {
320 this.frame.setTitle(Constants.NAME + " - " + jarName); 319 this.frame.setTitle(Constants.NAME + " - " + jarName);
321 this.classesPanel.removeAll(); 320 this.classesPanel.removeAll();
322 this.classesPanel.add(splitClasses); 321 this.classesPanel.add(splitClasses);
323 setSource(null); 322 setEditorText(null);
324 323
325 // update menu 324 // update menu
326 this.menuBar.closeJarMenu.setEnabled(true); 325 this.menuBar.closeJarMenu.setEnabled(true);
@@ -342,7 +341,7 @@ public class Gui {
342 this.frame.setTitle(Constants.NAME); 341 this.frame.setTitle(Constants.NAME);
343 setObfClasses(null); 342 setObfClasses(null);
344 setDeobfClasses(null); 343 setDeobfClasses(null);
345 setSource(null); 344 setEditorText(null);
346 this.classesPanel.removeAll(); 345 this.classesPanel.removeAll();
347 346
348 // update menu 347 // update menu
@@ -373,11 +372,16 @@ public class Gui {
373 this.menuBar.saveMappingsMenu.setEnabled(path != null); 372 this.menuBar.saveMappingsMenu.setEnabled(path != null);
374 } 373 }
375 374
376 public void setSource(String source) { 375 public void setEditorText(String source) {
377 this.editor.getHighlighter().removeAllHighlights(); 376 this.editor.getHighlighter().removeAllHighlights();
378 this.editor.setText(source); 377 this.editor.setText(source);
379 } 378 }
380 379
380 public void setSource(DecompiledClassSource source) {
381 editor.setText(source.toString());
382 setHighlightedTokens(source.getHighlightedTokens());
383 }
384
381 public void showToken(final Token token) { 385 public void showToken(final Token token) {
382 if (token == null) { 386 if (token == null) {
383 throw new IllegalArgumentException("Token cannot be null!"); 387 throw new IllegalArgumentException("Token cannot be null!");
@@ -401,15 +405,15 @@ public class Gui {
401 showToken(sortedTokens.get(0)); 405 showToken(sortedTokens.get(0));
402 } 406 }
403 407
404 public void setHighlightedTokens(Map<String, Iterable<Token>> tokens) { 408 public void setHighlightedTokens(Map<TokenHighlightType, Collection<Token>> tokens) {
405 // remove any old highlighters 409 // remove any old highlighters
406 this.editor.getHighlighter().removeAllHighlights(); 410 this.editor.getHighlighter().removeAllHighlights();
407 411
408 if (boxHighlightPainters != null) { 412 if (boxHighlightPainters != null) {
409 for (String s : tokens.keySet()) { 413 for (TokenHighlightType type : tokens.keySet()) {
410 BoxHighlightPainter painter = boxHighlightPainters.get(s); 414 BoxHighlightPainter painter = boxHighlightPainters.get(type);
411 if (painter != null) { 415 if (painter != null) {
412 setHighlightedTokens(tokens.get(s), painter); 416 setHighlightedTokens(tokens.get(type), painter);
413 } 417 }
414 } 418 }
415 } 419 }
@@ -435,17 +439,19 @@ public class Gui {
435 439
436 this.reference = reference; 440 this.reference = reference;
437 441
442 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference);
443
438 infoPanel.removeAll(); 444 infoPanel.removeAll();
439 if (reference.entry instanceof ClassEntry) { 445 if (translatedReference.entry instanceof ClassEntry) {
440 showClassEntry((ClassEntry) this.reference.entry); 446 showClassEntry((ClassEntry) translatedReference.entry);
441 } else if (this.reference.entry instanceof FieldEntry) { 447 } else if (translatedReference.entry instanceof FieldEntry) {
442 showFieldEntry((FieldEntry) this.reference.entry); 448 showFieldEntry((FieldEntry) translatedReference.entry);
443 } else if (this.reference.entry instanceof MethodEntry) { 449 } else if (translatedReference.entry instanceof MethodEntry) {
444 showMethodEntry((MethodEntry) this.reference.entry); 450 showMethodEntry((MethodEntry) translatedReference.entry);
445 } else if (this.reference.entry instanceof LocalVariableEntry) { 451 } else if (translatedReference.entry instanceof LocalVariableEntry) {
446 showLocalVariableEntry((LocalVariableEntry) this.reference.entry); 452 showLocalVariableEntry((LocalVariableEntry) translatedReference.entry);
447 } else { 453 } else {
448 throw new Error("Unknown entry desc: " + this.reference.entry.getClass().getName()); 454 throw new Error("Unknown entry desc: " + translatedReference.entry.getClass().getName());
449 } 455 }
450 456
451 redraw(); 457 redraw();
@@ -519,7 +525,7 @@ public class Gui {
519 Token token = this.controller.getToken(pos); 525 Token token = this.controller.getToken(pos);
520 boolean isToken = token != null; 526 boolean isToken = token != null;
521 527
522 reference = this.controller.getDeobfReference(token); 528 reference = this.controller.getReference(token);
523 529
524 Entry<?> referenceEntry = reference != null ? reference.entry : null; 530 Entry<?> referenceEntry = reference != null ? reference.entry : null;
525 boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry; 531 boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry;
@@ -527,7 +533,7 @@ public class Gui {
527 boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); 533 boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor();
528 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); 534 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor();
529 boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); 535 boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry);
530 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); 536 boolean isRenameable = isToken && this.controller.getDeobfuscator().isRenamable(reference);
531 537
532 if (isToken) { 538 if (isToken) {
533 showReference(reference); 539 showReference(reference);
@@ -544,7 +550,7 @@ public class Gui {
544 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 550 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation());
545 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); 551 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable);
546 552
547 if (isToken && this.controller.entryHasDeobfuscatedName(referenceEntry)) { 553 if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) {
548 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); 554 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated");
549 } else { 555 } else {
550 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); 556 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated");
@@ -576,7 +582,10 @@ public class Gui {
576 582
577 // init the text box 583 // init the text box
578 final JTextField text = new JTextField(); 584 final JTextField text = new JTextField();
579 text.setText(reference.getNameableName()); 585
586 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference);
587 text.setText(translatedReference.getNameableName());
588
580 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); 589 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height));
581 text.addKeyListener(new KeyAdapter() { 590 text.addKeyListener(new KeyAdapter() {
582 @Override 591 @Override
@@ -603,7 +612,7 @@ public class Gui {
603 612
604 int offset = text.getText().lastIndexOf('/') + 1; 613 int offset = text.getText().lastIndexOf('/') + 1;
605 // If it's a class and isn't in the default package, assume that it's deobfuscated. 614 // If it's a class and isn't in the default package, assume that it's deobfuscated.
606 if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) 615 if (translatedReference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0)
607 text.select(offset, text.getText().length()); 616 text.select(offset, text.getText().length());
608 else 617 else
609 text.selectAll(); 618 text.selectAll();
@@ -719,7 +728,7 @@ public class Gui {
719 } 728 }
720 729
721 public void toggleMapping() { 730 public void toggleMapping() {
722 if (this.controller.entryHasDeobfuscatedName(reference.entry)) { 731 if (this.controller.getDeobfuscator().isRemapped(reference.entry)) {
723 this.controller.removeMapping(reference); 732 this.controller.removeMapping(reference);
724 } else { 733 } else {
725 this.controller.markAsDeobfuscated(reference); 734 this.controller.markAsDeobfuscated(reference);
@@ -743,7 +752,7 @@ public class Gui {
743 callback.apply(response); 752 callback.apply(response);
744 } 753 }
745 754
746 public void saveMapping() throws IOException { 755 public void saveMapping() {
747 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) 756 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION)
748 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); 757 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath());
749 } 758 }
@@ -757,13 +766,8 @@ public class Gui {
757 // ask to save before closing 766 // ask to save before closing
758 showDiscardDiag((response) -> { 767 showDiscardDiag((response) -> {
759 if (response == JOptionPane.YES_OPTION) { 768 if (response == JOptionPane.YES_OPTION) {
760 try { 769 this.saveMapping();
761 this.saveMapping(); 770 this.frame.dispose();
762 this.frame.dispose();
763
764 } catch (IOException ex) {
765 throw new Error(ex);
766 }
767 } else if (response == JOptionPane.NO_OPTION) 771 } else if (response == JOptionPane.NO_OPTION)
768 this.frame.dispose(); 772 this.frame.dispose();
769 773
@@ -796,47 +800,39 @@ public class Gui {
796 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); 800 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false);
797 } 801 }
798 802
799 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName) { 803 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, String newName) {
800 String oldEntry = deobfReference.entry.getContainingClass().getPackageName(); 804 String oldEntry = obfReference.entry.getContainingClass().getPackageName();
801 String newEntry = new ClassEntry(newName).getPackageName(); 805 String newEntry = new ClassEntry(newName).getPackageName();
802 moveClassTree(deobfReference, newName, oldEntry == null, 806 moveClassTree(obfReference, oldEntry == null, newEntry == null);
803 newEntry == null);
804 } 807 }
805 808
806 // TODO: getExpansionState will *not* actually update itself based on name changes! 809 // TODO: getExpansionState will *not* actually update itself based on name changes!
807 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { 810 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, boolean isOldOb, boolean isNewOb) {
808 ClassEntry oldEntry = deobfReference.entry.getContainingClass(); 811 ClassEntry classEntry = obfReference.entry.getContainingClass();
809 ClassEntry newEntry = new ClassEntry(newName);
810 812
811 // Ob -> deob 813 // Ob -> deob
812 List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses); 814 List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses);
813 List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses); 815 List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses);
814 816
815 if (isOldOb && !isNewOb) { 817 if (isOldOb && !isNewOb) {
816 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); 818 this.deobfPanel.deobfClasses.moveClassIn(classEntry);
817 ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); 819 this.obfPanel.obfClasses.moveClassOut(classEntry);
818 this.obfPanel.obfClasses.removeNode(packageNode, oldEntry);
819 this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode);
820 this.deobfPanel.deobfClasses.reload(); 820 this.deobfPanel.deobfClasses.reload();
821 this.obfPanel.obfClasses.reload(); 821 this.obfPanel.obfClasses.reload();
822 } 822 }
823 // Deob -> ob 823 // Deob -> ob
824 else if (isNewOb && !isOldOb) { 824 else if (isNewOb && !isOldOb) {
825 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); 825 this.obfPanel.obfClasses.moveClassIn(classEntry);
826 ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); 826 this.deobfPanel.deobfClasses.moveClassOut(classEntry);
827 this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry);
828 this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode);
829 this.deobfPanel.deobfClasses.reload(); 827 this.deobfPanel.deobfClasses.reload();
830 this.obfPanel.obfClasses.reload(); 828 this.obfPanel.obfClasses.reload();
831 } 829 }
832 // Local move 830 // Local move
833 else if (isOldOb) { 831 else if (isOldOb) {
834 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); 832 this.obfPanel.obfClasses.moveClassIn(classEntry);
835 this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry));
836 this.obfPanel.obfClasses.reload(); 833 this.obfPanel.obfClasses.reload();
837 } else { 834 } else {
838 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); 835 this.deobfPanel.deobfClasses.moveClassIn(classEntry);
839 this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry));
840 this.deobfPanel.deobfClasses.reload(); 836 this.deobfPanel.deobfClasses.reload();
841 } 837 }
842 838
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index fd9e7f0c..03e17682 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -11,13 +11,13 @@
11 11
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import com.google.common.collect.ImmutableMap;
15import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
16import com.google.common.collect.Queues; 15import com.google.common.collect.Queues;
16import com.google.common.util.concurrent.ThreadFactoryBuilder;
17import com.strobel.decompiler.languages.java.ast.CompilationUnit; 17import com.strobel.decompiler.languages.java.ast.CompilationUnit;
18import cuchaz.enigma.Deobfuscator; 18import cuchaz.enigma.Deobfuscator;
19import cuchaz.enigma.SourceProvider;
19import cuchaz.enigma.analysis.*; 20import cuchaz.enigma.analysis.*;
20import cuchaz.enigma.api.EnigmaPlugin;
21import cuchaz.enigma.config.Config; 21import cuchaz.enigma.config.Config;
22import cuchaz.enigma.gui.dialog.ProgressDialog; 22import cuchaz.enigma.gui.dialog.ProgressDialog;
23import cuchaz.enigma.throwables.MappingParseException; 23import cuchaz.enigma.throwables.MappingParseException;
@@ -36,16 +36,20 @@ import java.awt.event.ItemEvent;
36import java.io.File; 36import java.io.File;
37import java.io.IOException; 37import java.io.IOException;
38import java.nio.file.Path; 38import java.nio.file.Path;
39import java.util.*; 39import java.util.Collection;
40import java.util.Deque;
41import java.util.List;
42import java.util.concurrent.ExecutorService;
43import java.util.concurrent.Executors;
40import java.util.jar.JarFile; 44import java.util.jar.JarFile;
41import java.util.stream.Collectors; 45import java.util.stream.Collectors;
42 46
43public class GuiController { 47public class GuiController {
48 private static final ExecutorService DECOMPILER_SERVICE = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("decompiler-thread").build());
44 49
45 private Deobfuscator deobfuscator; 50 private Deobfuscator deobfuscator;
46 private Gui gui; 51 private Gui gui;
47 private SourceIndex index; 52 private DecompiledClassSource currentSource;
48 private ClassEntry currentObfClass;
49 private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; 53 private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack;
50 54
51 private Path loadedMappingPath; 55 private Path loadedMappingPath;
@@ -54,8 +58,7 @@ public class GuiController {
54 public GuiController(Gui gui) { 58 public GuiController(Gui gui) {
55 this.gui = gui; 59 this.gui = gui;
56 this.deobfuscator = null; 60 this.deobfuscator = null;
57 this.index = null; 61 this.currentSource = null;
58 this.currentObfClass = null;
59 this.referenceStack = Queues.newArrayDeque(); 62 this.referenceStack = Queues.newArrayDeque();
60 } 63 }
61 64
@@ -93,7 +96,7 @@ public class GuiController {
93 public void saveMappings(MappingFormat format, Path path) { 96 public void saveMappings(MappingFormat format, Path path) {
94 EntryRemapper mapper = deobfuscator.getMapper(); 97 EntryRemapper mapper = deobfuscator.getMapper();
95 98
96 MappingDelta delta = mapper.takeMappingDelta(); 99 MappingDelta<EntryMapping> delta = mapper.takeMappingDelta();
97 boolean saveAll = !path.equals(loadedMappingPath); 100 boolean saveAll = !path.equals(loadedMappingPath);
98 101
99 ProgressDialog.runInThread(this.gui.getFrame(), progress -> { 102 ProgressDialog.runInThread(this.gui.getFrame(), progress -> {
@@ -116,189 +119,167 @@ public class GuiController {
116 } 119 }
117 120
118 public void exportSource(final File dirOut) { 121 public void exportSource(final File dirOut) {
119 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); 122 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut.toPath(), progress));
120 } 123 }
121 124
122 public void exportJar(final File fileOut) { 125 public void exportJar(final File fileOut) {
123 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); 126 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeTransformedJar(fileOut, progress));
124 } 127 }
125 128
126 public Token getToken(int pos) { 129 public Token getToken(int pos) {
127 if (this.index == null) { 130 if (this.currentSource == null) {
128 return null; 131 return null;
129 } 132 }
130 return this.index.getReferenceToken(pos); 133 return this.currentSource.getIndex().getReferenceToken(pos);
131 } 134 }
132 135
133 @Nullable 136 @Nullable
134 public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { 137 public EntryReference<Entry<?>, Entry<?>> getReference(Token token) {
135 if (this.index == null) { 138 if (this.currentSource == null) {
136 return null; 139 return null;
137 } 140 }
138 return this.index.getDeobfReference(token); 141 return this.currentSource.getIndex().getReference(token);
139 } 142 }
140 143
141 public ReadableToken getReadableToken(Token token) { 144 public ReadableToken getReadableToken(Token token) {
142 if (this.index == null) { 145 if (this.currentSource == null) {
143 return null; 146 return null;
144 } 147 }
148 SourceIndex index = this.currentSource.getIndex();
145 return new ReadableToken( 149 return new ReadableToken(
146 this.index.getLineNumber(token.start), 150 index.getLineNumber(token.start),
147 this.index.getColumnNumber(token.start), 151 index.getColumnNumber(token.start),
148 this.index.getColumnNumber(token.end) 152 index.getColumnNumber(token.end)
149 ); 153 );
150 } 154 }
151 155
152 public boolean entryHasDeobfuscatedName(Entry<?> deobfEntry) { 156 public boolean entryIsInJar(Entry<?> entry) {
153 EntryResolver resolver = this.deobfuscator.getMapper().getDeobfResolver(); 157 if (entry == null) return false;
154 Entry<?> resolvedEntry = resolver.resolveFirstEntry(deobfEntry, ResolutionStrategy.RESOLVE_ROOT); 158 return this.deobfuscator.isRenamable(entry);
155 return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.getMapper().obfuscate(resolvedEntry));
156 } 159 }
157 160
158 public boolean entryIsInJar(Entry<?> deobfEntry) { 161 public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) {
159 if (deobfEntry == null) return false;
160 return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.getMapper().obfuscate(deobfEntry));
161 }
162
163 public boolean referenceIsRenameable(EntryReference<Entry<?>, Entry<?>> deobfReference) {
164 if (deobfReference == null) return false;
165 return this.deobfuscator.isRenameable(this.deobfuscator.getMapper().obfuscate(deobfReference));
166 }
167
168 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
169 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
170 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 162 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
171 ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, obfClassEntry); 163 ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, entry);
172 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); 164 return ClassInheritanceTreeNode.findNode(rootNode, entry);
173 } 165 }
174 166
175 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { 167 public ClassImplementationsTreeNode getClassImplementations(ClassEntry entry) {
176 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
177 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 168 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
178 return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, obfClassEntry); 169 return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, entry);
179 } 170 }
180 171
181 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { 172 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry entry) {
182 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
183 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 173 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
184 MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, obfMethodEntry); 174 MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, entry);
185 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); 175 return MethodInheritanceTreeNode.findNode(rootNode, entry);
186 } 176 }
187 177
188 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { 178 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) {
189 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
190 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 179 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
191 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, obfMethodEntry); 180 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, entry);
192 if (rootNodes.isEmpty()) { 181 if (rootNodes.isEmpty()) {
193 return null; 182 return null;
194 } 183 }
195 if (rootNodes.size() > 1) { 184 if (rootNodes.size() > 1) {
196 System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); 185 System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one.");
197 } 186 }
198 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); 187 return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry);
199 } 188 }
200 189
201 public ClassReferenceTreeNode getClassReferences(ClassEntry deobfClassEntry) { 190 public ClassReferenceTreeNode getClassReferences(ClassEntry entry) {
202 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
203 Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); 191 Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator();
204 ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, obfClassEntry); 192 ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, entry);
205 rootNode.load(this.deobfuscator.getJarIndex(), true); 193 rootNode.load(this.deobfuscator.getJarIndex(), true);
206 return rootNode; 194 return rootNode;
207 } 195 }
208 196
209 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { 197 public FieldReferenceTreeNode getFieldReferences(FieldEntry entry) {
210 FieldEntry obfFieldEntry = this.deobfuscator.getMapper().obfuscate(deobfFieldEntry);
211 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 198 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
212 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, obfFieldEntry); 199 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, entry);
213 rootNode.load(this.deobfuscator.getJarIndex(), true); 200 rootNode.load(this.deobfuscator.getJarIndex(), true);
214 return rootNode; 201 return rootNode;
215 } 202 }
216 203
217 public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry, boolean recursive) { 204 public MethodReferenceTreeNode getMethodReferences(MethodEntry entry, boolean recursive) {
218 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
219 Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); 205 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
220 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, obfMethodEntry); 206 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, entry);
221 rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); 207 rootNode.load(this.deobfuscator.getJarIndex(), true, recursive);
222 return rootNode; 208 return rootNode;
223 } 209 }
224 210
225 public void rename(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean refreshClassTree) { 211 public void rename(EntryReference<Entry<?>, Entry<?>> reference, String newName, boolean refreshClassTree) {
226 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); 212 this.deobfuscator.rename(reference.getNameableEntry(), newName);
227 this.deobfuscator.rename(obfReference.getNameableEntry(), newName);
228
229 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
230 this.gui.moveClassTree(deobfReference, newName);
231 refreshCurrentClass(obfReference);
232 213
214 if (refreshClassTree && reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass())
215 this.gui.moveClassTree(reference, newName);
216 refreshCurrentClass(reference);
233 } 217 }
234 218
235 public void removeMapping(EntryReference<Entry<?>, Entry<?>> deobfReference) { 219 public void removeMapping(EntryReference<Entry<?>, Entry<?>> reference) {
236 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); 220 this.deobfuscator.removeMapping(reference.getNameableEntry());
237 this.deobfuscator.removeMapping(obfReference.getNameableEntry()); 221 if (reference.entry instanceof ClassEntry)
238 if (deobfReference.entry instanceof ClassEntry) 222 this.gui.moveClassTree(reference, false, true);
239 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); 223 refreshCurrentClass(reference);
240 refreshCurrentClass(obfReference);
241 } 224 }
242 225
243 public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> deobfReference) { 226 public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> reference) {
244 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); 227 this.deobfuscator.markAsDeobfuscated(reference.getNameableEntry());
245 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); 228 if (reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass())
246 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) 229 this.gui.moveClassTree(reference, true, false);
247 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); 230 refreshCurrentClass(reference);
248 refreshCurrentClass(obfReference);
249 } 231 }
250 232
251 public void openDeclaration(Entry<?> deobfEntry) { 233 public void openDeclaration(Entry<?> entry) {
252 if (deobfEntry == null) { 234 if (entry == null) {
253 throw new IllegalArgumentException("Entry cannot be null!"); 235 throw new IllegalArgumentException("Entry cannot be null!");
254 } 236 }
255 openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); 237 openReference(new EntryReference<>(entry, entry.getName()));
256 } 238 }
257 239
258 public void openReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { 240 public void openReference(EntryReference<Entry<?>, Entry<?>> reference) {
259 if (deobfReference == null) { 241 if (reference == null) {
260 throw new IllegalArgumentException("Reference cannot be null!"); 242 throw new IllegalArgumentException("Reference cannot be null!");
261 } 243 }
262 244
263 // get the reference target class 245 // get the reference target class
264 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); 246 ClassEntry classEntry = reference.getLocationClassEntry();
265 ClassEntry obfClassEntry = obfReference.getLocationClassEntry(); 247 if (!this.deobfuscator.isRenamable(classEntry)) {
266 if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { 248 throw new IllegalArgumentException("Obfuscated class " + classEntry + " was not found in the jar!");
267 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!");
268 } 249 }
269 if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { 250
251 if (this.currentSource == null || !this.currentSource.getEntry().equals(classEntry)) {
270 // deobfuscate the class, then navigate to the reference 252 // deobfuscate the class, then navigate to the reference
271 this.currentObfClass = obfClassEntry; 253 loadClass(classEntry, () -> showReference(reference));
272 deobfuscate(this.currentObfClass, obfReference);
273 } else { 254 } else {
274 showReference(obfReference); 255 showReference(reference);
275 } 256 }
276 } 257 }
277 258
278 private void showReference(EntryReference<Entry<?>, Entry<?>> obfReference) { 259 private void showReference(EntryReference<Entry<?>, Entry<?>> reference) {
279 EntryRemapper mapper = this.deobfuscator.getMapper(); 260 EntryRemapper mapper = this.deobfuscator.getMapper();
280 261
281 Collection<Token> tokens = mapper.getObfResolver().resolveReference(obfReference, ResolutionStrategy.RESOLVE_ROOT) 262 SourceIndex index = this.currentSource.getIndex();
263 Collection<Token> tokens = mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_ROOT)
282 .stream() 264 .stream()
283 .map(mapper::deobfuscate) 265 .flatMap(r -> index.getReferenceTokens(r).stream())
284 .flatMap(reference -> index.getReferenceTokens(reference).stream())
285 .collect(Collectors.toList()); 266 .collect(Collectors.toList());
286 267
287 if (tokens.isEmpty()) { 268 if (tokens.isEmpty()) {
288 // DEBUG 269 // DEBUG
289 System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentObfClass)); 270 System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentSource.getEntry()));
290 } else { 271 } else {
291 this.gui.showTokens(tokens); 272 this.gui.showTokens(tokens);
292 } 273 }
293 } 274 }
294 275
295 public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { 276 public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> reference) {
296 this.referenceStack.push(this.deobfuscator.getMapper().obfuscate(deobfReference)); 277 this.referenceStack.push(reference);
297 } 278 }
298 279
299 public void openPreviousReference() { 280 public void openPreviousReference() {
300 if (hasPreviousLocation()) { 281 if (hasPreviousLocation()) {
301 openReference(this.deobfuscator.getMapper().deobfuscate(this.referenceStack.pop())); 282 openReference(this.referenceStack.pop());
302 } 283 }
303 } 284 }
304 285
@@ -318,97 +299,65 @@ public class GuiController {
318 refreshCurrentClass(null); 299 refreshCurrentClass(null);
319 } 300 }
320 301
321 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> obfReference) { 302 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) {
322 if (this.currentObfClass != null) { 303 if (currentSource != null) {
323 deobfuscate(this.currentObfClass, obfReference); 304 loadClass(currentSource.getEntry(), () -> {
305 if (reference != null) {
306 showReference(reference);
307 }
308 });
324 } 309 }
325 } 310 }
326 311
327 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry<?>, Entry<?>> obfReference) { 312 private void loadClass(ClassEntry classEntry, Runnable callback) {
313 ClassEntry targetClass = classEntry.getOutermostClass();
328 314
329 this.gui.setSource("(deobfuscating...)"); 315 boolean requiresDecompile = currentSource == null || !currentSource.getEntry().equals(targetClass);
316 if (requiresDecompile) {
317 gui.setEditorText("(decompiling...)");
318 }
330 319
331 // run the deobfuscator in a separate thread so we don't block the GUI event queue 320 DECOMPILER_SERVICE.submit(() -> {
332 new Thread(() -> 321 try {
333 { 322 if (requiresDecompile) {
334 // decompile,deobfuscate the bytecode 323 decompileSource(targetClass, deobfuscator.getObfSourceProvider());
335 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClass().getFullName());
336 if (sourceTree == null) {
337 // decompilation of this class is not supported
338 gui.setSource("Unable to find class: " + classEntry);
339 return;
340 }
341 String source = deobfuscator.getSource(sourceTree);
342 index = deobfuscator.getSourceIndex(sourceTree, source);
343
344 String sourceString = index.getSource();
345
346 // set the highlighted tokens
347 List<Token> obfuscatedTokens = Lists.newArrayList();
348 List<Token> proposedTokens = Lists.newArrayList();
349 List<Token> deobfuscatedTokens = Lists.newArrayList();
350 List<Token> otherTokens = Lists.newArrayList();
351
352 int offset = 0;
353 Map<Token, Token> tokenRemap = new HashMap<>();
354 boolean remapped = false;
355
356 for (Token inToken : index.referenceTokens()) {
357 Token token = inToken.move(offset);
358
359 EntryReference<Entry<?>, Entry<?>> reference = index.getDeobfReference(inToken);
360 if (referenceIsRenameable(reference)) {
361 boolean added = false;
362
363 if (!entryHasDeobfuscatedName(reference.getNameableEntry())) {
364 Entry<?> obfEntry = deobfuscator.getMapper().obfuscate(reference.getNameableEntry());
365 if (obfEntry instanceof FieldEntry) {
366 for (EnigmaPlugin plugin : deobfuscator.getPlugins()) {
367 String owner = obfEntry.getContainingClass().getFullName();
368 String proposal = plugin.proposeFieldName(owner, obfEntry.getName(), ((FieldEntry) obfEntry).getDesc().toString());
369 if (proposal != null) {
370 proposedTokens.add(token);
371 offset += token.getRenameOffset(proposal);
372 sourceString = token.rename(sourceString, proposal);
373 added = true;
374 remapped = true;
375 break;
376 }
377 }
378 }
379 }
380
381 if (!added) {
382 if (entryHasDeobfuscatedName(reference.getNameableEntry())) {
383 deobfuscatedTokens.add(token);
384 } else {
385 obfuscatedTokens.add(token);
386 }
387 }
388 } else {
389 otherTokens.add(token);
390 } 324 }
391 325
392 tokenRemap.put(inToken, token); 326 remapSource(deobfuscator.getMapper().getDeobfuscator());
327 callback.run();
328 } catch (Throwable t) {
329 System.err.println("An exception was thrown while decompiling class " + classEntry.getFullName());
330 t.printStackTrace(System.err);
393 } 331 }
332 });
333 }
394 334
395 if (remapped) { 335 private void decompileSource(ClassEntry targetClass, SourceProvider sourceProvider) {
396 index.remap(sourceString, tokenRemap); 336 CompilationUnit sourceTree = sourceProvider.getSources(targetClass.getFullName());
397 } 337 if (sourceTree == null) {
338 gui.setEditorText("Unable to find class: " + targetClass);
339 return;
340 }
398 341
399 gui.setSource(sourceString); 342 DropImportAstTransform.INSTANCE.run(sourceTree);
400 if (obfReference != null) { 343
401 showReference(obfReference); 344 String sourceString = sourceProvider.writeSourceToString(sourceTree);
402 } 345
346 SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true);
347 index.resolveReferences(deobfuscator.getMapper().getObfResolver());
348
349 currentSource = new DecompiledClassSource(targetClass, deobfuscator, index);
350 }
351
352 private void remapSource(Translator translator) {
353 if (currentSource == null) {
354 return;
355 }
356
357 currentSource.remapSource(translator);
403 358
404 gui.setEditorTheme(Config.getInstance().lookAndFeel); 359 gui.setEditorTheme(Config.getInstance().lookAndFeel);
405 gui.setHighlightedTokens(ImmutableMap.of( 360 gui.setSource(currentSource);
406 "obfuscated", obfuscatedTokens,
407 "proposed", proposedTokens,
408 "deobfuscated", deobfuscatedTokens,
409 "other", otherTokens
410 ));
411 }).start();
412 } 361 }
413 362
414 public Deobfuscator getDeobfuscator() { 363 public Deobfuscator getDeobfuscator() {
diff --git a/src/main/java/cuchaz/enigma/gui/SourceRemapper.java b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java
new file mode 100644
index 00000000..f38f44ea
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java
@@ -0,0 +1,64 @@
1package cuchaz.enigma.gui;
2
3import cuchaz.enigma.analysis.Token;
4
5import java.util.HashMap;
6import java.util.Map;
7
8public class SourceRemapper {
9 private final String source;
10 private final Iterable<Token> tokens;
11
12 public SourceRemapper(String source, Iterable<Token> tokens) {
13 this.source = source;
14 this.tokens = tokens;
15 }
16
17 public Result remap(Remapper remapper) {
18 StringBuffer remappedSource = new StringBuffer(source);
19 Map<Token, Token> remappedTokens = new HashMap<>();
20
21 int accumulatedOffset = 0;
22 for (Token token : tokens) {
23 Token movedToken = token.move(accumulatedOffset);
24
25 String remappedName = remapper.remap(token, movedToken);
26 if (remappedName != null) {
27 accumulatedOffset += movedToken.getRenameOffset(remappedName);
28 movedToken.rename(remappedSource, remappedName);
29 }
30
31 if (!token.equals(movedToken)) {
32 remappedTokens.put(token, movedToken);
33 }
34 }
35
36 return new Result(remappedSource.toString(), remappedTokens);
37 }
38
39 public static class Result {
40 private final String remappedSource;
41 private final Map<Token, Token> remappedTokens;
42
43 Result(String remappedSource, Map<Token, Token> remappedTokens) {
44 this.remappedSource = remappedSource;
45 this.remappedTokens = remappedTokens;
46 }
47
48 public String getSource() {
49 return remappedSource;
50 }
51
52 public Token getRemappedToken(Token token) {
53 return remappedTokens.getOrDefault(token, token);
54 }
55
56 public boolean isEmpty() {
57 return remappedTokens.isEmpty();
58 }
59 }
60
61 public interface Remapper {
62 String remap(Token token, Token movedToken);
63 }
64}
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index f4f02776..dfbfa650 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -152,12 +152,8 @@ public class MenuBar extends JMenuBar {
152 if (this.gui.getController().isDirty()) { 152 if (this.gui.getController().isDirty()) {
153 this.gui.showDiscardDiag((response -> { 153 this.gui.showDiscardDiag((response -> {
154 if (response == JOptionPane.YES_OPTION) { 154 if (response == JOptionPane.YES_OPTION) {
155 try { 155 gui.saveMapping();
156 gui.saveMapping(); 156 this.gui.getController().closeMappings();
157 this.gui.getController().closeMappings();
158 } catch (IOException e) {
159 throw new Error(e);
160 }
161 } else if (response == JOptionPane.NO_OPTION) 157 } else if (response == JOptionPane.NO_OPTION)
162 this.gui.getController().closeMappings(); 158 this.gui.getController().closeMappings();
163 return null; 159 return null;
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
index 10366ced..cef64943 100644
--- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
@@ -34,7 +34,9 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter {
34 public static Rectangle getBounds(JTextComponent text, int start, int end) { 34 public static Rectangle getBounds(JTextComponent text, int start, int end) {
35 try { 35 try {
36 // determine the bounds of the text 36 // determine the bounds of the text
37 Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); 37 Rectangle startRect = text.modelToView(start);
38 Rectangle endRect = text.modelToView(end);
39 Rectangle bounds = startRect.union(endRect);
38 40
39 // adjust the box so it looks nice 41 // adjust the box so it looks nice
40 bounds.x -= 2; 42 bounds.x -= 2;
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java
new file mode 100644
index 00000000..ae23f324
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java
@@ -0,0 +1,7 @@
1package cuchaz.enigma.gui.highlight;
2
3public enum TokenHighlightType {
4 OBFUSCATED,
5 DEOBFUSCATED,
6 PROPOSED
7}
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index bf6b1788..922f8f24 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -17,13 +17,19 @@ import javax.swing.tree.DefaultMutableTreeNode;
17 17
18public class ClassSelectorClassNode extends DefaultMutableTreeNode { 18public class ClassSelectorClassNode extends DefaultMutableTreeNode {
19 19
20 private final ClassEntry obfEntry;
20 private ClassEntry classEntry; 21 private ClassEntry classEntry;
21 22
22 public ClassSelectorClassNode(ClassEntry classEntry) { 23 public ClassSelectorClassNode(ClassEntry obfEntry, ClassEntry classEntry) {
24 this.obfEntry = obfEntry;
23 this.classEntry = classEntry; 25 this.classEntry = classEntry;
24 this.setUserObject(classEntry); 26 this.setUserObject(classEntry);
25 } 27 }
26 28
29 public ClassEntry getObfEntry() {
30 return obfEntry;
31 }
32
27 public ClassEntry getClassEntry() { 33 public ClassEntry getClassEntry() {
28 return this.classEntry; 34 return this.classEntry;
29 } 35 }
diff --git a/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java b/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
new file mode 100644
index 00000000..18c966cd
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
@@ -0,0 +1,44 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.NameValidator;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5
6import java.util.Collection;
7import java.util.Locale;
8
9public class LocalNameGenerator {
10 public static String generateArgumentName(int index, TypeDescriptor desc, Collection<TypeDescriptor> arguments) {
11 boolean uniqueType = arguments.stream().filter(desc::equals).count() <= 1;
12 String translatedName;
13 int nameIndex = index + 1;
14 StringBuilder nameBuilder = new StringBuilder(getTypeName(desc));
15 if (!uniqueType || NameValidator.isReserved(nameBuilder.toString())) {
16 nameBuilder.append(nameIndex);
17 }
18 translatedName = nameBuilder.toString();
19 return translatedName;
20 }
21
22 public static String generateLocalVariableName(int index, TypeDescriptor desc) {
23 int nameIndex = index + 1;
24 return getTypeName(desc) + nameIndex;
25 }
26
27 private static String getTypeName(TypeDescriptor desc) {
28 // Unfortunately each of these have different name getters, so they have different code paths
29 if (desc.isPrimitive()) {
30 TypeDescriptor.Primitive argCls = desc.getPrimitive();
31 return argCls.name().toLowerCase(Locale.ROOT);
32 } else if (desc.isArray()) {
33 // List types would require this whole block again, so just go with aListx
34 return "arr";
35 } else if (desc.isType()) {
36 String typeName = desc.getTypeEntry().getSimpleName().replace("$", "");
37 typeName = typeName.substring(0, 1).toLowerCase(Locale.ROOT) + typeName.substring(1);
38 return typeName;
39 } else {
40 System.err.println("Encountered invalid argument type descriptor " + desc.toString());
41 return "var";
42 }
43 }
44}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
index b7d8d17e..1203aba0 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
@@ -6,7 +6,6 @@ import cuchaz.enigma.translation.Translatable;
6import cuchaz.enigma.translation.Translator; 6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; 7import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
8import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
10import cuchaz.enigma.translation.mapping.tree.HashEntryTree; 9import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
11import cuchaz.enigma.translation.representation.entry.Entry; 10import cuchaz.enigma.translation.representation.entry.Entry;
12 11
@@ -14,59 +13,25 @@ import javax.annotation.Nullable;
14import java.util.Collection; 13import java.util.Collection;
15 14
16public class EntryRemapper { 15public class EntryRemapper {
17 private final EntryTree<EntryMapping> obfToDeobf; 16 private final DeltaTrackingTree<EntryMapping> obfToDeobf;
18 private final DeltaTrackingTree<EntryMapping> deobfToObf;
19
20 private final JarIndex obfIndex;
21 17
22 private final EntryResolver obfResolver; 18 private final EntryResolver obfResolver;
23 private EntryResolver deobfResolver;
24
25 private final Translator deobfuscator; 19 private final Translator deobfuscator;
26 private Translator obfuscator;
27 20
28 private final MappingValidator validator; 21 private final MappingValidator validator;
29 22
30 private EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf, EntryTree<EntryMapping> deobfToObf) { 23 public EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf) {
31 this.obfToDeobf = obfToDeobf; 24 this.obfToDeobf = new DeltaTrackingTree<>(obfToDeobf);
32 this.deobfToObf = new DeltaTrackingTree<>(deobfToObf);
33 25
34 this.obfIndex = jarIndex;
35 this.obfResolver = jarIndex.getEntryResolver(); 26 this.obfResolver = jarIndex.getEntryResolver();
36 27
37 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver); 28 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver);
38 rebuildDeobfIndex();
39 29
40 this.validator = new MappingValidator(this.deobfToObf, deobfuscator, obfResolver); 30 this.validator = new MappingValidator(obfToDeobf, deobfuscator, obfResolver);
41 } 31 }
42 32
43 public EntryRemapper(JarIndex jarIndex) { 33 public EntryRemapper(JarIndex jarIndex) {
44 this(jarIndex, new HashEntryTree<>(), new HashEntryTree<>()); 34 this(jarIndex, new HashEntryTree<>());
45 }
46
47 public EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> deobfuscationTrees) {
48 this(jarIndex, deobfuscationTrees, inverse(deobfuscationTrees));
49 }
50
51 private static EntryTree<EntryMapping> inverse(EntryTree<EntryMapping> tree) {
52 Translator translator = new MappingTranslator(tree, VoidEntryResolver.INSTANCE);
53 EntryTree<EntryMapping> inverse = new HashEntryTree<>();
54
55 // Naive approach, could operate on the nodes of the tree. However, this runs infrequently.
56 Collection<Entry<?>> entries = tree.getAllEntries();
57 for (Entry<?> sourceEntry : entries) {
58 Entry<?> targetEntry = translator.translate(sourceEntry);
59 inverse.insert(targetEntry, new EntryMapping(sourceEntry.getName()));
60 }
61
62 return inverse;
63 }
64
65 private void rebuildDeobfIndex() {
66 JarIndex deobfIndex = obfIndex.remapped(deobfuscator);
67
68 this.deobfResolver = deobfIndex.getEntryResolver();
69 this.obfuscator = new MappingTranslator(deobfToObf, deobfResolver);
70 } 35 }
71 36
72 public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { 37 public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) {
@@ -76,84 +41,31 @@ public class EntryRemapper {
76 validator.validateRename(resolvedEntry, deobfMapping.getTargetName()); 41 validator.validateRename(resolvedEntry, deobfMapping.getTargetName());
77 } 42 }
78 43
79 setObfToDeobf(resolvedEntry, deobfMapping); 44 obfToDeobf.insert(obfuscatedEntry, deobfMapping);
80 } 45 }
81
82 // Temporary hack, not very performant
83 rebuildDeobfIndex();
84 }
85
86 public <E extends Entry<?>> void mapFromDeobf(E deobfuscatedEntry, @Nullable EntryMapping deobfMapping) {
87 E obfuscatedEntry = obfuscate(deobfuscatedEntry);
88 mapFromObf(obfuscatedEntry, deobfMapping);
89 } 46 }
90 47
91 public void removeByObf(Entry<?> obfuscatedEntry) { 48 public void removeByObf(Entry<?> obfuscatedEntry) {
92 mapFromObf(obfuscatedEntry, null); 49 mapFromObf(obfuscatedEntry, null);
93 } 50 }
94 51
95 public void removeByDeobf(Entry<?> deobfuscatedEntry) {
96 mapFromObf(obfuscate(deobfuscatedEntry), null);
97 }
98
99 private <E extends Entry<?>> void setObfToDeobf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) {
100 E prevDeobf = deobfuscate(obfuscatedEntry);
101 obfToDeobf.insert(obfuscatedEntry, deobfMapping);
102
103 E newDeobf = deobfuscate(obfuscatedEntry);
104
105 // Reconstruct the children of this node in the deobf -> obf tree with our new mapping
106 // We only need to do this for deobf -> obf because the obf tree is always consistent on the left hand side
107 // We lookup by obf, and the obf never changes. This is not the case for deobf so we need to update the tree.
108
109 EntryTreeNode<EntryMapping> node = deobfToObf.findNode(prevDeobf);
110 if (node != null) {
111 for (EntryTreeNode<EntryMapping> child : node.getNodesRecursively()) {
112 Entry<?> entry = child.getEntry();
113 EntryMapping mapping = new EntryMapping(obfuscate(entry).getName());
114
115 deobfToObf.insert(entry.replaceAncestor(prevDeobf, newDeobf), mapping);
116 deobfToObf.remove(entry);
117 }
118 } else {
119 deobfToObf.insert(newDeobf, new EntryMapping(obfuscatedEntry.getName()));
120 }
121 }
122
123 @Nullable 52 @Nullable
124 public EntryMapping getDeobfMapping(Entry<?> entry) { 53 public EntryMapping getDeobfMapping(Entry<?> entry) {
125 return obfToDeobf.get(entry); 54 return obfToDeobf.get(entry);
126 } 55 }
127 56
128 @Nullable
129 public EntryMapping getObfMapping(Entry<?> entry) {
130 return deobfToObf.get(entry);
131 }
132
133 public boolean hasDeobfMapping(Entry<?> obfEntry) { 57 public boolean hasDeobfMapping(Entry<?> obfEntry) {
134 return obfToDeobf.contains(obfEntry); 58 return obfToDeobf.contains(obfEntry);
135 } 59 }
136 60
137 public boolean hasObfMapping(Entry<?> deobfEntry) {
138 return deobfToObf.contains(deobfEntry);
139 }
140
141 public <T extends Translatable> T deobfuscate(T translatable) { 61 public <T extends Translatable> T deobfuscate(T translatable) {
142 return deobfuscator.translate(translatable); 62 return deobfuscator.translate(translatable);
143 } 63 }
144 64
145 public <T extends Translatable> T obfuscate(T translatable) {
146 return obfuscator.translate(translatable);
147 }
148
149 public Translator getDeobfuscator() { 65 public Translator getDeobfuscator() {
150 return deobfuscator; 66 return deobfuscator;
151 } 67 }
152 68
153 public Translator getObfuscator() {
154 return obfuscator;
155 }
156
157 public Collection<Entry<?>> getObfEntries() { 69 public Collection<Entry<?>> getObfEntries() {
158 return obfToDeobf.getAllEntries(); 70 return obfToDeobf.getAllEntries();
159 } 71 }
@@ -162,40 +74,23 @@ public class EntryRemapper {
162 return obfToDeobf.getRootEntries(); 74 return obfToDeobf.getRootEntries();
163 } 75 }
164 76
165 public Collection<Entry<?>> getDeobfEntries() {
166 return deobfToObf.getAllEntries();
167 }
168
169 public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) { 77 public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) {
170 return obfToDeobf.getChildren(obfuscatedEntry); 78 return obfToDeobf.getChildren(obfuscatedEntry);
171 } 79 }
172 80
173 public Collection<Entry<?>> getDeobfChildren(Entry<?> deobfuscatedEntry) { 81 public DeltaTrackingTree<EntryMapping> getObfToDeobf() {
174 return deobfToObf.getChildren(deobfuscatedEntry);
175 }
176
177 public EntryTree<EntryMapping> getObfToDeobf() {
178 return obfToDeobf; 82 return obfToDeobf;
179 } 83 }
180 84
181 public DeltaTrackingTree<EntryMapping> getDeobfToObf() { 85 public MappingDelta<EntryMapping> takeMappingDelta() {
182 return deobfToObf; 86 return obfToDeobf.takeDelta();
183 }
184
185 public MappingDelta takeMappingDelta() {
186 MappingDelta delta = deobfToObf.takeDelta();
187 return delta.translate(obfuscator, VoidEntryResolver.INSTANCE, deobfToObf);
188 } 87 }
189 88
190 public boolean isDirty() { 89 public boolean isDirty() {
191 return deobfToObf.isDirty(); 90 return obfToDeobf.isDirty();
192 } 91 }
193 92
194 public EntryResolver getObfResolver() { 93 public EntryResolver getObfResolver() {
195 return obfResolver; 94 return obfResolver;
196 } 95 }
197
198 public EntryResolver getDeobfResolver() {
199 return deobfResolver;
200 }
201} 96}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
index 4fba49d5..9f1f468b 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
@@ -6,28 +6,35 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
6import cuchaz.enigma.translation.mapping.tree.EntryTree; 6import cuchaz.enigma.translation.mapping.tree.EntryTree;
7import cuchaz.enigma.translation.representation.entry.Entry; 7import cuchaz.enigma.translation.representation.entry.Entry;
8 8
9public class MappingDelta implements Translatable { 9public class MappingDelta<T> implements Translatable {
10 public static final Object PLACEHOLDER = new Object(); 10 public static final Object PLACEHOLDER = new Object();
11 11
12 private final EntryTree<T> baseMappings;
13
12 private final EntryTree<Object> additions; 14 private final EntryTree<Object> additions;
13 private final EntryTree<Object> deletions; 15 private final EntryTree<Object> deletions;
14 16
15 public MappingDelta(EntryTree<Object> additions, EntryTree<Object> deletions) { 17 public MappingDelta(EntryTree<T> baseMappings, EntryTree<Object> additions, EntryTree<Object> deletions) {
18 this.baseMappings = baseMappings;
16 this.additions = additions; 19 this.additions = additions;
17 this.deletions = deletions; 20 this.deletions = deletions;
18 } 21 }
19 22
20 public MappingDelta() { 23 public MappingDelta(EntryTree<T> baseMappings) {
21 this(new HashEntryTree<>(), new HashEntryTree<>()); 24 this(baseMappings, new HashEntryTree<>(), new HashEntryTree<>());
22 } 25 }
23 26
24 public static MappingDelta added(EntryTree<EntryMapping> mappings) { 27 public static <T> MappingDelta<T> added(EntryTree<T> mappings) {
25 EntryTree<Object> additions = new HashEntryTree<>(); 28 EntryTree<Object> additions = new HashEntryTree<>();
26 for (Entry<?> entry : mappings.getAllEntries()) { 29 for (Entry<?> entry : mappings.getAllEntries()) {
27 additions.insert(entry, PLACEHOLDER); 30 additions.insert(entry, PLACEHOLDER);
28 } 31 }
29 32
30 return new MappingDelta(additions, new HashEntryTree<>()); 33 return new MappingDelta<>(new HashEntryTree<>(), additions, new HashEntryTree<>());
34 }
35
36 public EntryTree<T> getBaseMappings() {
37 return baseMappings;
31 } 38 }
32 39
33 public EntryTree<?> getAdditions() { 40 public EntryTree<?> getAdditions() {
@@ -39,18 +46,11 @@ public class MappingDelta implements Translatable {
39 } 46 }
40 47
41 @Override 48 @Override
42 public MappingDelta translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { 49 public MappingDelta<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
43 return new MappingDelta( 50 return new MappingDelta<>(
44 translate(translator, additions), 51 translator.translate(baseMappings),
45 translate(translator, deletions) 52 translator.translate(additions),
53 translator.translate(deletions)
46 ); 54 );
47 } 55 }
48
49 private EntryTree<Object> translate(Translator translator, EntryTree<Object> tree) {
50 EntryTree<Object> translatedTree = new HashEntryTree<>();
51 for (Entry<?> entry : tree.getAllEntries()) {
52 translatedTree.insert(translator.translate(entry), PLACEHOLDER);
53 }
54 return translatedTree;
55 }
56} 56}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
index 422bf380..9be48c3a 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
@@ -6,14 +6,15 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.translation.representation.entry.Entry; 6import cuchaz.enigma.translation.representation.entry.Entry;
7 7
8import java.util.Collection; 8import java.util.Collection;
9import java.util.stream.Collectors;
9 10
10public class MappingValidator { 11public class MappingValidator {
11 private final EntryTree<EntryMapping> deobfToObf; 12 private final EntryTree<EntryMapping> obfToDeobf;
12 private final Translator deobfuscator; 13 private final Translator deobfuscator;
13 private final EntryResolver entryResolver; 14 private final EntryResolver entryResolver;
14 15
15 public MappingValidator(EntryTree<EntryMapping> deobfToObf, Translator deobfuscator, EntryResolver entryResolver) { 16 public MappingValidator(EntryTree<EntryMapping> obfToDeobf, Translator deobfuscator, EntryResolver entryResolver) {
16 this.deobfToObf = deobfToObf; 17 this.obfToDeobf = obfToDeobf;
17 this.deobfuscator = deobfuscator; 18 this.deobfuscator = deobfuscator;
18 this.entryResolver = entryResolver; 19 this.entryResolver = entryResolver;
19 } 20 }
@@ -28,8 +29,11 @@ public class MappingValidator {
28 29
29 private void validateUnique(Entry<?> entry, String name) { 30 private void validateUnique(Entry<?> entry, String name) {
30 Entry<?> translatedEntry = deobfuscator.translate(entry); 31 Entry<?> translatedEntry = deobfuscator.translate(entry);
31 Collection<Entry<?>> siblings = deobfToObf.getSiblings(translatedEntry); 32 Collection<Entry<?>> translatedSiblings = obfToDeobf.getSiblings(entry).stream()
32 if (!isUnique(translatedEntry, siblings, name)) { 33 .map(deobfuscator::translate)
34 .collect(Collectors.toList());
35
36 if (!isUnique(translatedEntry, translatedSiblings, name)) {
33 throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!"); 37 throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!");
34 } 38 }
35 } 39 }
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
index 5acb1da2..1d44b6e8 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
@@ -14,10 +14,7 @@ package cuchaz.enigma.translation.mapping.serde;
14import cuchaz.enigma.ProgressListener; 14import cuchaz.enigma.ProgressListener;
15import cuchaz.enigma.translation.MappingTranslator; 15import cuchaz.enigma.translation.MappingTranslator;
16import cuchaz.enigma.translation.Translator; 16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.AccessModifier; 17import cuchaz.enigma.translation.mapping.*;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.MappingDelta;
20import cuchaz.enigma.translation.mapping.VoidEntryResolver;
21import cuchaz.enigma.translation.mapping.tree.EntryTree; 18import cuchaz.enigma.translation.mapping.tree.EntryTree;
22import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 19import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
23import cuchaz.enigma.translation.representation.entry.*; 20import cuchaz.enigma.translation.representation.entry.*;
@@ -37,7 +34,7 @@ import java.util.stream.Collectors;
37public enum EnigmaMappingsWriter implements MappingsWriter { 34public enum EnigmaMappingsWriter implements MappingsWriter {
38 FILE { 35 FILE {
39 @Override 36 @Override
40 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { 37 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) {
41 Collection<ClassEntry> classes = mappings.getRootEntries().stream() 38 Collection<ClassEntry> classes = mappings.getRootEntries().stream()
42 .filter(entry -> entry instanceof ClassEntry) 39 .filter(entry -> entry instanceof ClassEntry)
43 .map(entry -> (ClassEntry) entry) 40 .map(entry -> (ClassEntry) entry)
@@ -58,8 +55,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
58 }, 55 },
59 DIRECTORY { 56 DIRECTORY {
60 @Override 57 @Override
61 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { 58 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) {
62 applyDeletions(delta.getDeletions(), path); 59 applyDeletions(delta.getBaseMappings(), delta.getDeletions(), path);
63 60
64 Collection<ClassEntry> classes = delta.getAdditions().getRootEntries().stream() 61 Collection<ClassEntry> classes = delta.getAdditions().getRootEntries().stream()
65 .filter(entry -> entry instanceof ClassEntry) 62 .filter(entry -> entry instanceof ClassEntry)
@@ -76,8 +73,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
76 73
77 try { 74 try {
78 Path classPath = resolve(path, translator.translate(classEntry)); 75 Path classPath = resolve(path, translator.translate(classEntry));
79 Files.deleteIfExists(classPath);
80 Files.createDirectories(classPath.getParent()); 76 Files.createDirectories(classPath.getParent());
77 Files.deleteIfExists(classPath);
81 78
82 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) { 79 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) {
83 writeRoot(writer, mappings, classEntry); 80 writeRoot(writer, mappings, classEntry);
@@ -89,10 +86,12 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
89 }); 86 });
90 } 87 }
91 88
92 private void applyDeletions(EntryTree<?> deletions, Path root) { 89 private void applyDeletions(EntryTree<EntryMapping> baseMappings, EntryTree<?> deletions, Path root) {
90 Translator oldMappingTranslator = new MappingTranslator(baseMappings, VoidEntryResolver.INSTANCE);
91
93 Collection<ClassEntry> deletedClasses = deletions.getRootEntries().stream() 92 Collection<ClassEntry> deletedClasses = deletions.getRootEntries().stream()
94 .filter(e -> e instanceof ClassEntry) 93 .filter(e -> e instanceof ClassEntry)
95 .map(e -> (ClassEntry) e) 94 .map(e -> oldMappingTranslator.translate((ClassEntry) e))
96 .collect(Collectors.toList()); 95 .collect(Collectors.toList());
97 96
98 for (ClassEntry classEntry : deletedClasses) { 97 for (ClassEntry classEntry : deletedClasses) {
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
index 4db16454..622a0e15 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
@@ -28,7 +28,7 @@ public enum MappingFormat {
28 write(mappings, MappingDelta.added(mappings), path, progressListener); 28 write(mappings, MappingDelta.added(mappings), path, progressListener);
29 } 29 }
30 30
31 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progressListener) { 31 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progressListener) {
32 if (writer == null) { 32 if (writer == null) {
33 throw new IllegalStateException(name() + " does not support writing"); 33 throw new IllegalStateException(name() + " does not support writing");
34 } 34 }
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
index b5196681..77f6ee03 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
@@ -8,5 +8,9 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree;
8import java.nio.file.Path; 8import java.nio.file.Path;
9 9
10public interface MappingsWriter { 10public interface MappingsWriter {
11 void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress); 11 void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress);
12
13 default void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progress) {
14 write(mappings, MappingDelta.added(mappings), path, progress);
15 }
12} 16}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
index 5ff91413..40be136a 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
@@ -29,7 +29,7 @@ public enum SrgMappingsWriter implements MappingsWriter {
29 INSTANCE; 29 INSTANCE;
30 30
31 @Override 31 @Override
32 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { 32 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) {
33 try { 33 try {
34 Files.deleteIfExists(path); 34 Files.deleteIfExists(path);
35 Files.createFile(path); 35 Files.createFile(path);
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
index 98a01df2..36be5e1e 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
@@ -1,5 +1,9 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver;
3import cuchaz.enigma.translation.mapping.MappingDelta; 7import cuchaz.enigma.translation.mapping.MappingDelta;
4import cuchaz.enigma.translation.representation.entry.Entry; 8import cuchaz.enigma.translation.representation.entry.Entry;
5 9
@@ -10,11 +14,13 @@ import java.util.Iterator;
10public class DeltaTrackingTree<T> implements EntryTree<T> { 14public class DeltaTrackingTree<T> implements EntryTree<T> {
11 private final EntryTree<T> delegate; 15 private final EntryTree<T> delegate;
12 16
17 private EntryTree<T> deltaReference;
13 private EntryTree<Object> additions = new HashEntryTree<>(); 18 private EntryTree<Object> additions = new HashEntryTree<>();
14 private EntryTree<Object> deletions = new HashEntryTree<>(); 19 private EntryTree<Object> deletions = new HashEntryTree<>();
15 20
16 public DeltaTrackingTree(EntryTree<T> delegate) { 21 public DeltaTrackingTree(EntryTree<T> delegate) {
17 this.delegate = delegate; 22 this.delegate = delegate;
23 this.deltaReference = new HashEntryTree<>(delegate);
18 } 24 }
19 25
20 public DeltaTrackingTree() { 26 public DeltaTrackingTree() {
@@ -40,7 +46,7 @@ public class DeltaTrackingTree<T> implements EntryTree<T> {
40 } 46 }
41 47
42 public void trackAddition(Entry<?> entry) { 48 public void trackAddition(Entry<?> entry) {
43 deletions.remove(entry); 49 deletions.insert(entry, MappingDelta.PLACEHOLDER);
44 additions.insert(entry, MappingDelta.PLACEHOLDER); 50 additions.insert(entry, MappingDelta.PLACEHOLDER);
45 } 51 }
46 52
@@ -82,6 +88,14 @@ public class DeltaTrackingTree<T> implements EntryTree<T> {
82 } 88 }
83 89
84 @Override 90 @Override
91 public DeltaTrackingTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
92 DeltaTrackingTree<T> translatedTree = new DeltaTrackingTree<>(delegate.translate(translator, resolver, mappings));
93 translatedTree.additions = additions.translate(translator, resolver, mappings);
94 translatedTree.deletions = deletions.translate(translator, resolver, mappings);
95 return translatedTree;
96 }
97
98 @Override
85 public Collection<Entry<?>> getAllEntries() { 99 public Collection<Entry<?>> getAllEntries() {
86 return delegate.getAllEntries(); 100 return delegate.getAllEntries();
87 } 101 }
@@ -96,13 +110,14 @@ public class DeltaTrackingTree<T> implements EntryTree<T> {
96 return delegate.iterator(); 110 return delegate.iterator();
97 } 111 }
98 112
99 public MappingDelta takeDelta() { 113 public MappingDelta<T> takeDelta() {
100 MappingDelta delta = new MappingDelta(additions, deletions); 114 MappingDelta<T> delta = new MappingDelta<>(deltaReference, additions, deletions);
101 resetDelta(); 115 resetDelta();
102 return delta; 116 return delta;
103 } 117 }
104 118
105 private void resetDelta() { 119 private void resetDelta() {
120 deltaReference = new HashEntryTree<>(delegate);
106 additions = new HashEntryTree<>(); 121 additions = new HashEntryTree<>();
107 deletions = new HashEntryTree<>(); 122 deletions = new HashEntryTree<>();
108 } 123 }
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
index 73fe12d0..4f341f45 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
@@ -1,12 +1,16 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import cuchaz.enigma.translation.Translatable;
4import cuchaz.enigma.translation.Translator;
3import cuchaz.enigma.translation.mapping.EntryMap; 5import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryResolver;
4import cuchaz.enigma.translation.representation.entry.Entry; 8import cuchaz.enigma.translation.representation.entry.Entry;
5 9
6import javax.annotation.Nullable; 10import javax.annotation.Nullable;
7import java.util.Collection; 11import java.util.Collection;
8 12
9public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>> { 13public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>>, Translatable {
10 Collection<Entry<?>> getChildren(Entry<?> entry); 14 Collection<Entry<?>> getChildren(Entry<?> entry);
11 15
12 Collection<Entry<?>> getSiblings(Entry<?> entry); 16 Collection<Entry<?>> getSiblings(Entry<?> entry);
@@ -17,4 +21,7 @@ public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>> {
17 Collection<EntryTreeNode<T>> getAllNodes(); 21 Collection<EntryTreeNode<T>> getAllNodes();
18 22
19 Collection<Entry<?>> getRootEntries(); 23 Collection<Entry<?>> getRootEntries();
24
25 @Override
26 EntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings);
20} 27}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
index ff88bf94..551fb1c8 100644
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
@@ -1,5 +1,9 @@
1package cuchaz.enigma.translation.mapping.tree; 1package cuchaz.enigma.translation.mapping.tree;
2 2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver;
3import cuchaz.enigma.translation.representation.entry.Entry; 7import cuchaz.enigma.translation.representation.entry.Entry;
4 8
5import javax.annotation.Nullable; 9import javax.annotation.Nullable;
@@ -9,6 +13,15 @@ import java.util.stream.Collectors;
9public class HashEntryTree<T> implements EntryTree<T> { 13public class HashEntryTree<T> implements EntryTree<T> {
10 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>(); 14 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>();
11 15
16 public HashEntryTree() {
17 }
18
19 public HashEntryTree(EntryTree<T> tree) {
20 for (EntryTreeNode<T> node : tree.getAllNodes()) {
21 insert(node.getEntry(), node.getValue());
22 }
23 }
24
12 @Override 25 @Override
13 public void insert(Entry<?> entry, T value) { 26 public void insert(Entry<?> entry, T value) {
14 List<HashTreeNode<T>> path = computePath(entry); 27 List<HashTreeNode<T>> path = computePath(entry);
@@ -156,4 +169,13 @@ public class HashEntryTree<T> implements EntryTree<T> {
156 public boolean isEmpty() { 169 public boolean isEmpty() {
157 return root.isEmpty(); 170 return root.isEmpty();
158 } 171 }
172
173 @Override
174 public HashEntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
175 HashEntryTree<T> translatedTree = new HashEntryTree<>();
176 for (EntryTreeNode<T> node : getAllNodes()) {
177 translatedTree.insert(translator.translate(node.getEntry()), node.getValue());
178 }
179 return translatedTree;
180 }
159} 181}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java
index 9c9fa3d3..a9ec5fac 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java
@@ -17,29 +17,23 @@ import com.strobel.assembler.metadata.MethodDefinition;
17import cuchaz.enigma.translation.representation.entry.*; 17import cuchaz.enigma.translation.representation.entry.*;
18 18
19public class ProcyonEntryFactory { 19public class ProcyonEntryFactory {
20 private final ReferencedEntryPool entryPool;
21
22 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
23 this.entryPool = entryPool;
24 }
25
26 public FieldEntry getFieldEntry(MemberReference def) { 20 public FieldEntry getFieldEntry(MemberReference def) {
27 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); 21 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
28 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature()); 22 return new FieldEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()));
29 } 23 }
30 24
31 public FieldDefEntry getFieldDefEntry(FieldDefinition def) { 25 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
32 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); 26 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
33 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 27 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
34 } 28 }
35 29
36 public MethodEntry getMethodEntry(MemberReference def) { 30 public MethodEntry getMethodEntry(MemberReference def) {
37 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); 31 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
38 return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature()); 32 return new MethodEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()));
39 } 33 }
40 34
41 public MethodDefEntry getMethodDefEntry(MethodDefinition def) { 35 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
42 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); 36 ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName());
43 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 37 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
44 } 38 }
45} 39}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java
deleted file mode 100644
index 631b3754..00000000
--- a/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java
+++ /dev/null
@@ -1,60 +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.translation.representation;
13
14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17
18import java.util.HashMap;
19import java.util.Map;
20
21public class ReferencedEntryPool {
22 private final Map<String, ClassEntry> classEntries = new HashMap<>();
23 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
24 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
25
26 public ClassEntry getClass(String name) {
27 // TODO: FIXME - I'm a hack!
28 if ("[T".equals(name) || "[[T".equals(name) || "[[[T".equals(name)) {
29 name = name.replaceAll("T", "Ljava/lang/Object;");
30 }
31
32 final String computeName = name;
33 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(computeName));
34 }
35
36 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
37 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
38 }
39
40 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
41 String key = name + desc.toString();
42 return getClassMethods(ownerEntry.getFullName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
43 }
44
45 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
46 return getField(ownerEntry, name, new TypeDescriptor(desc));
47 }
48
49 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
50 return getClassFields(ownerEntry.getFullName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
51 }
52
53 private Map<String, MethodEntry> getClassMethods(String name) {
54 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
55 }
56
57 private Map<String, FieldEntry> getClassFields(String name) {
58 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
59 }
60}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/Signature.java b/src/main/java/cuchaz/enigma/translation/representation/Signature.java
index dc241b7a..424088ab 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/Signature.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/Signature.java
@@ -3,9 +3,9 @@ package cuchaz.enigma.translation.representation;
3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; 3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
4import cuchaz.enigma.translation.Translatable; 4import cuchaz.enigma.translation.Translatable;
5import cuchaz.enigma.translation.Translator; 5import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryResolver; 8import cuchaz.enigma.translation.mapping.EntryResolver;
8import cuchaz.enigma.translation.mapping.EntryMap;
9import cuchaz.enigma.translation.representation.entry.ClassEntry; 9import cuchaz.enigma.translation.representation.entry.ClassEntry;
10import org.objectweb.asm.signature.SignatureReader; 10import org.objectweb.asm.signature.SignatureReader;
11import org.objectweb.asm.signature.SignatureVisitor; 11import org.objectweb.asm.signature.SignatureVisitor;
@@ -78,7 +78,12 @@ public class Signature implements Translatable {
78 78
79 @Override 79 @Override
80 public int hashCode() { 80 public int hashCode() {
81 return signature.hashCode() | (isType ? 1 : 0) << 16; 81 int hash = (isType ? 1 : 0) << 16;
82 if (signature != null) {
83 hash |= signature.hashCode();
84 }
85
86 return hash;
82 } 87 }
83 88
84 @Override 89 @Override
diff --git a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
index f7ba849e..719d693a 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
@@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.strobel.assembler.metadata.TypeReference;
16import cuchaz.enigma.translation.Translatable; 17import cuchaz.enigma.translation.Translatable;
17import cuchaz.enigma.translation.Translator; 18import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryMapping; 19import cuchaz.enigma.translation.mapping.EntryMapping;
@@ -111,6 +112,10 @@ public class TypeDescriptor implements Translatable {
111 return new TypeDescriptor("L" + name + ";"); 112 return new TypeDescriptor("L" + name + ";");
112 } 113 }
113 114
115 public static TypeDescriptor parse(TypeReference type) {
116 return new TypeDescriptor(type.getErasedSignature());
117 }
118
114 @Override 119 @Override
115 public String toString() { 120 public String toString() {
116 return this.desc; 121 return this.desc;
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
index 5904efe6..644658fd 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
@@ -17,6 +17,7 @@ import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping; 17import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.mapping.NameValidator; 18import cuchaz.enigma.translation.mapping.NameValidator;
19 19
20import javax.annotation.Nonnull;
20import javax.annotation.Nullable; 21import javax.annotation.Nullable;
21import java.util.List; 22import java.util.List;
22import java.util.Objects; 23import java.util.Objects;
@@ -125,6 +126,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
125 return parent; 126 return parent;
126 } 127 }
127 128
129 @Nonnull
128 public ClassEntry getOutermostClass() { 130 public ClassEntry getOutermostClass() {
129 if (parent == null) { 131 if (parent == null) {
130 return this; 132 return this;
@@ -181,6 +183,15 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
181 } 183 }
182 184
183 @Override 185 @Override
186 public String getSourceRemapName() {
187 ClassEntry outerClass = getOuterClass();
188 if (outerClass != null) {
189 return outerClass.getSourceRemapName() + "." + name;
190 }
191 return getSimpleName();
192 }
193
194 @Override
184 public int compareTo(ClassEntry entry) { 195 public int compareTo(ClassEntry entry) {
185 return name.compareTo(entry.name); 196 return name.compareTo(entry.name);
186 } 197 }
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
index 1a2ca785..227400eb 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
@@ -22,6 +22,10 @@ import java.util.List;
22public interface Entry<P extends Entry<?>> extends Translatable { 22public interface Entry<P extends Entry<?>> extends Translatable {
23 String getName(); 23 String getName();
24 24
25 default String getSourceRemapName() {
26 return getName();
27 }
28
25 @Nullable 29 @Nullable
26 P getParent(); 30 P getParent();
27 31
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
index df96b599..0c12f1c8 100644
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
@@ -17,11 +17,6 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co
17 protected final int index; 17 protected final int index;
18 protected final boolean parameter; 18 protected final boolean parameter;
19 19
20 @Deprecated
21 public LocalVariableEntry(MethodEntry parent, int index, String name) {
22 this(parent, index, name, true);
23 }
24
25 public LocalVariableEntry(MethodEntry parent, int index, String name, boolean parameter) { 20 public LocalVariableEntry(MethodEntry parent, int index, String name, boolean parameter) {
26 super(parent, name); 21 super(parent, name);
27 22
@@ -37,7 +32,7 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co
37 return MethodEntry.class; 32 return MethodEntry.class;
38 } 33 }
39 34
40 public boolean isParameter() { 35 public boolean isArgument() {
41 return this.parameter; 36 return this.parameter;
42 } 37 }
43 38
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java
index 25cb60c5..14b1418d 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfed.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfed.java
@@ -68,27 +68,28 @@ public class TestDeobfed {
68 public void decompile() 68 public void decompile()
69 throws Exception { 69 throws Exception {
70 Deobfuscator deobfuscator = new Deobfuscator(jar); 70 Deobfuscator deobfuscator = new Deobfuscator(jar);
71 deobfuscator.getSourceTree("a"); 71 SourceProvider sourceProvider = deobfuscator.getObfSourceProvider();
72 deobfuscator.getSourceTree("b"); 72 sourceProvider.getSources("a");
73 deobfuscator.getSourceTree("c"); 73 sourceProvider.getSources("b");
74 deobfuscator.getSourceTree("d"); 74 sourceProvider.getSources("c");
75 deobfuscator.getSourceTree("d$1"); 75 sourceProvider.getSources("d");
76 deobfuscator.getSourceTree("e"); 76 sourceProvider.getSources("d$1");
77 deobfuscator.getSourceTree("f"); 77 sourceProvider.getSources("e");
78 deobfuscator.getSourceTree("g"); 78 sourceProvider.getSources("f");
79 deobfuscator.getSourceTree("g$a"); 79 sourceProvider.getSources("g");
80 deobfuscator.getSourceTree("g$a$a"); 80 sourceProvider.getSources("g$a");
81 deobfuscator.getSourceTree("g$b"); 81 sourceProvider.getSources("g$a$a");
82 deobfuscator.getSourceTree("g$b$a"); 82 sourceProvider.getSources("g$b");
83 deobfuscator.getSourceTree("h"); 83 sourceProvider.getSources("g$b$a");
84 deobfuscator.getSourceTree("h$a"); 84 sourceProvider.getSources("h");
85 deobfuscator.getSourceTree("h$a$a"); 85 sourceProvider.getSources("h$a");
86 deobfuscator.getSourceTree("h$b"); 86 sourceProvider.getSources("h$a$a");
87 deobfuscator.getSourceTree("h$b$a"); 87 sourceProvider.getSources("h$b");
88 deobfuscator.getSourceTree("h$b$a$a"); 88 sourceProvider.getSources("h$b$a");
89 deobfuscator.getSourceTree("h$b$a$b"); 89 sourceProvider.getSources("h$b$a$a");
90 deobfuscator.getSourceTree("i"); 90 sourceProvider.getSources("h$b$a$b");
91 deobfuscator.getSourceTree("i$a"); 91 sourceProvider.getSources("i");
92 deobfuscator.getSourceTree("i$b"); 92 sourceProvider.getSources("i$a");
93 sourceProvider.getSources("i$b");
93 } 94 }
94} 95}
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
index 5f117213..e070b66f 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
@@ -51,6 +51,7 @@ public class TestDeobfuscator {
51 public void decompileClass() 51 public void decompileClass()
52 throws Exception { 52 throws Exception {
53 Deobfuscator deobfuscator = getDeobfuscator(); 53 Deobfuscator deobfuscator = getDeobfuscator();
54 deobfuscator.getSource(deobfuscator.getSourceTree("a")); 54 SourceProvider sourceProvider = deobfuscator.getObfSourceProvider();
55 sourceProvider.writeSourceToString(sourceProvider.getSources("a"));
55 } 56 }
56} 57}
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 0319cf61..8738fd79 100644
--- a/src/test/java/cuchaz/enigma/TestInnerClasses.java
+++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java
@@ -79,6 +79,6 @@ public class TestInnerClasses {
79 } 79 }
80 80
81 private void decompile(ClassEntry classEntry) { 81 private void decompile(ClassEntry classEntry) {
82 deobfuscator.getSourceTree(classEntry.getName()); 82 deobfuscator.getObfSourceProvider().getSources(classEntry.getName());
83 } 83 }
84} 84}
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java
index a5f5f712..ce5d6316 100644
--- a/src/test/java/cuchaz/enigma/TestSourceIndex.java
+++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java
@@ -13,6 +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.analysis.SourceIndex;
16import cuchaz.enigma.translation.representation.entry.ClassEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
17import org.junit.Test; 18import org.junit.Test;
18 19
@@ -50,11 +51,13 @@ public class TestSourceIndex {
50 } 51 }
51 } 52 }
52 53
54 SourceProvider sourceProvider = deobfuscator.getObfSourceProvider();
53 for (ClassEntry obfClassEntry : classEntries) { 55 for (ClassEntry obfClassEntry : classEntries) {
54 try { 56 try {
55 CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); 57 CompilationUnit tree = sourceProvider.getSources(obfClassEntry.getName());
56 String source = deobfuscator.getSource(tree); 58 String source = sourceProvider.writeSourceToString(tree);
57 deobfuscator.getSourceIndex(tree, source); 59
60 SourceIndex.buildIndex(source, tree, true);
58 } catch (Throwable t) { 61 } catch (Throwable t) {
59 throw new Error("Unable to index " + obfClassEntry, t); 62 throw new Error("Unable to index " + obfClassEntry, t);
60 } 63 }
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java
index aac28662..c4670a20 100644
--- a/src/test/java/cuchaz/enigma/TokenChecker.java
+++ b/src/test/java/cuchaz/enigma/TokenChecker.java
@@ -34,11 +34,12 @@ public class TokenChecker {
34 34
35 protected String getDeclarationToken(Entry<?> entry) { 35 protected String getDeclarationToken(Entry<?> entry) {
36 // decompile the class 36 // decompile the class
37 CompilationUnit tree = deobfuscator.getSourceTree(entry.getContainingClass().getFullName()); 37 SourceProvider sourceProvider = deobfuscator.getObfSourceProvider();
38 CompilationUnit tree = sourceProvider.getSources(entry.getContainingClass().getFullName());
38 // DEBUG 39 // DEBUG
39 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); 40 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null );
40 String source = deobfuscator.getSource(tree); 41 String source = sourceProvider.writeSourceToString(tree);
41 SourceIndex index = deobfuscator.getSourceIndex(tree, source); 42 SourceIndex index = SourceIndex.buildIndex(source, tree, true);
42 43
43 // get the token value 44 // get the token value
44 Token token = index.getDeclarationToken(entry); 45 Token token = index.getDeclarationToken(entry);
@@ -51,9 +52,10 @@ public class TokenChecker {
51 @SuppressWarnings("unchecked") 52 @SuppressWarnings("unchecked")
52 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) { 53 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) {
53 // decompile the class 54 // decompile the class
54 CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getContainingClass().getFullName()); 55 SourceProvider sourceProvider = deobfuscator.getObfSourceProvider();
55 String source = deobfuscator.getSource(tree); 56 CompilationUnit tree = sourceProvider.getSources(reference.context.getContainingClass().getFullName());
56 SourceIndex index = deobfuscator.getSourceIndex(tree, source); 57 String source = sourceProvider.writeSourceToString(tree);
58 SourceIndex index = SourceIndex.buildIndex(source, tree, true);
57 59
58 // get the token values 60 // get the token values
59 List<String> values = Lists.newArrayList(); 61 List<String> values = Lists.newArrayList();