summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Gegy2019-01-24 14:48:32 +0200
committerGravatar Adrian Siekierka2019-01-24 13:48:32 +0100
commit00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch)
tree6f9e4c24dbcc6d118fceec56adf7bf9d747a485c
parentmark as 0.13.0-SNAPSHOT for preliminary development (diff)
downloadenigma-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.gz
enigma-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.xz
enigma-00fcd0550fcdda621c2e4662f6ddd55ce673b931.zip
[WIP] Mapping rework (#91)
* Move packages * Mapping & entry refactor: first pass * Fix deobf -> obf tree remapping * Resolve various issues * Give all entries the potential for parents and treat inner classes as children * Deobf UI tree elements * Tests pass * Sort mapping output * Fix delta tracking * Index separation and first pass for #97 * Keep track of remapped jar index * Fix child entries not being remapped * Drop non-root entries * Track dropped mappings * Fix enigma mapping ordering * EntryTreeNode interface * Small tweaks * Naive full index remap on rename * Entries can resolve to more than one root entry * Support alternative resolution strategies * Bridge method resolution * Tests pass * Fix mappings being used where there are none * Fix methods with different descriptors being considered unique. closes #89
-rw-r--r--src/main/java/cuchaz/enigma/CommandMain.java57
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java483
-rw-r--r--src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java2
-rw-r--r--src/main/java/cuchaz/enigma/Main.java13
-rw-r--r--src/main/java/cuchaz/enigma/ProgressListener.java17
-rw-r--r--src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java2
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java55
-rw-r--r--src/main/java/cuchaz/enigma/analysis/Access.java2
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java35
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java32
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java30
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryReference.java34
-rw-r--r--src/main/java/cuchaz/enigma/analysis/EntryRenamer.java167
-rw-r--r--src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java39
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java29
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java87
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java583
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java48
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java46
-rw-r--r--src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java67
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ParsedJar.java10
-rw-r--r--src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java4
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java53
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java15
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java19
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java8
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java275
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java77
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java109
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java (renamed from src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java)31
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java97
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java165
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java24
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java83
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java9
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java46
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java20
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java74
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java10
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java55
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java5
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java57
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java80
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java209
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiTricks.java42
-rw-r--r--src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java44
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java45
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java627
-rw-r--r--src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java371
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java100
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java58
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java268
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java101
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java186
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java160
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java365
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java80
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java130
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java21
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java210
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java109
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java38
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java175
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/Entry.java22
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java49
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java44
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java77
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java61
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java93
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java80
-rw-r--r--src/main/java/cuchaz/enigma/translation/MappingTranslator.java24
-rw-r--r--src/main/java/cuchaz/enigma/translation/SignatureUpdater.java (renamed from src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java)2
-rw-r--r--src/main/java/cuchaz/enigma/translation/Translatable.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/TranslationDirection.java (renamed from src/main/java/cuchaz/enigma/mapping/TranslationDirection.java)2
-rw-r--r--src/main/java/cuchaz/enigma/translation/Translator.java54
-rw-r--r--src/main/java/cuchaz/enigma/translation/VoidTranslator.java10
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java25
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java24
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java30
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java201
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java41
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java225
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java56
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java28
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java45
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java91
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java (renamed from src/main/java/cuchaz/enigma/mapping/NameValidator.java)41
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java6
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java27
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java260
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java260
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java54
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java12
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java12
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java115
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java100
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java113
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java20
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java36
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java159
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java72
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java (renamed from src/main/java/cuchaz/enigma/bytecode/AccessFlags.java)9
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java (renamed from src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java)24
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java (renamed from src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java)7
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java (renamed from src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java)11
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/Signature.java (renamed from src/main/java/cuchaz/enigma/mapping/Signature.java)15
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java (renamed from src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java)20
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java92
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java180
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java7
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java99
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java61
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java86
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java45
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java92
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java (renamed from src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java)34
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java95
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java71
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfed.java9
-rw-r--r--src/test/java/cuchaz/enigma/TestDeobfuscator.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestEntryFactory.java8
-rw-r--r--src/test/java/cuchaz/enigma/TestInnerClasses.java44
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java31
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java118
-rw-r--r--src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java93
-rw-r--r--src/test/java/cuchaz/enigma/TestMethodDescriptor.java4
-rw-r--r--src/test/java/cuchaz/enigma/TestSourceIndex.java4
-rw-r--r--src/test/java/cuchaz/enigma/TestTokensConstructors.java2
-rw-r--r--src/test/java/cuchaz/enigma/TestTranslator.java15
-rw-r--r--src/test/java/cuchaz/enigma/TestTypeDescriptor.java2
-rw-r--r--src/test/java/cuchaz/enigma/TokenChecker.java12
137 files changed, 4651 insertions, 5893 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java
index 6c8caa43..a84cd5e7 100644
--- a/src/main/java/cuchaz/enigma/CommandMain.java
+++ b/src/main/java/cuchaz/enigma/CommandMain.java
@@ -11,11 +11,15 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.Deobfuscator.ProgressListener; 14import cuchaz.enigma.translation.mapping.EntryMapping;
15import cuchaz.enigma.mapping.Mappings; 15import cuchaz.enigma.translation.mapping.serde.MappingFormat;
16import cuchaz.enigma.mapping.MappingsEnigmaReader; 16import cuchaz.enigma.translation.mapping.tree.EntryTree;
17 17
18import java.io.File; 18import java.io.File;
19import java.nio.file.Files;
20import java.nio.file.Path;
21import java.nio.file.Paths;
22import java.util.Locale;
19import java.util.jar.JarFile; 23import java.util.jar.JarFile;
20 24
21public class CommandMain { 25public class CommandMain {
@@ -52,7 +56,7 @@ public class CommandMain {
52 private static void decompile(String[] args) throws Exception { 56 private static void decompile(String[] args) throws Exception {
53 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 57 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
54 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); 58 File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true));
55 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 59 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false));
56 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 60 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
57 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); 61 deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener());
58 } 62 }
@@ -60,43 +64,47 @@ public class CommandMain {
60 private static void deobfuscate(String[] args) throws Exception { 64 private static void deobfuscate(String[] args) throws Exception {
61 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); 65 File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true));
62 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); 66 File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true));
63 File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); 67 Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false));
64 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); 68 Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn));
65 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); 69 deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener());
66 } 70 }
67 71
68 private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { 72 private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception {
69 System.out.println("Reading jar..."); 73 System.out.println("Reading jar...");
70 Deobfuscator deobfuscator = new Deobfuscator(jar); 74 Deobfuscator deobfuscator = new Deobfuscator(jar);
71 if (fileMappings != null) { 75 if (fileMappings != null) {
72 System.out.println("Reading mappings..."); 76 System.out.println("Reading mappings...");
73 Mappings mappings = new MappingsEnigmaReader().read(fileMappings); 77 EntryTree<EntryMapping> mappings = chooseEnigmaFormat(fileMappings).read(fileMappings);
74 deobfuscator.setMappings(mappings); 78 deobfuscator.setMappings(mappings);
75 } 79 }
76 return deobfuscator; 80 return deobfuscator;
77 } 81 }
78 82
79 private static void convertMappings(String[] args) throws Exception { 83 private static void convertMappings(String[] args) throws Exception {
80 File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true)); 84 Path fileMappings = getReadablePath(getArg(args, 1, "enigma mapping", true));
81 File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); 85 File result = getWritableFile(getArg(args, 2, "enigma mapping", true));
82 String name = getArg(args, 3, "format desc", true); 86 String name = getArg(args, 3, "format desc", true);
83 Mappings.FormatType formatType; 87 MappingFormat saveFormat;
84 try { 88 try {
85 formatType = Mappings.FormatType.valueOf(name.toUpperCase()); 89 saveFormat = MappingFormat.valueOf(name.toUpperCase(Locale.ROOT));
86 } catch (IllegalArgumentException e) { 90 } catch (IllegalArgumentException e) {
87 throw new IllegalArgumentException(name + "is not a valid mapping format!"); 91 throw new IllegalArgumentException(name + "is not a valid mapping format!");
88 } 92 }
89 93
90 System.out.println("Reading mappings..."); 94 System.out.println("Reading mappings...");
91 Mappings mappings = new MappingsEnigmaReader().read(fileMappings); 95
96 MappingFormat readFormat = chooseEnigmaFormat(fileMappings);
97 EntryTree<EntryMapping> mappings = readFormat.read(fileMappings);
92 System.out.println("Saving new mappings..."); 98 System.out.println("Saving new mappings...");
93 switch (formatType) { 99
94 case SRG_FILE: 100 saveFormat.write(mappings, result.toPath(), new ConsoleProgressListener());
95 mappings.saveSRGMappings(result); 101 }
96 break; 102
97 default: 103 private static MappingFormat chooseEnigmaFormat(Path path) {
98 mappings.saveEnigmaMappings(result, Mappings.FormatType.ENIGMA_FILE != formatType); 104 if (Files.isDirectory(path)) {
99 break; 105 return MappingFormat.ENIGMA_DIRECTORY;
106 } else {
107 return MappingFormat.ENIGMA_FILE;
100 } 108 }
101 } 109 }
102 110
@@ -149,6 +157,17 @@ public class CommandMain {
149 return file; 157 return file;
150 } 158 }
151 159
160 private static Path getReadablePath(String path) {
161 if (path == null) {
162 return null;
163 }
164 Path file = Paths.get(path).toAbsolutePath();
165 if (!Files.exists(file)) {
166 throw new IllegalArgumentException("Cannot find file: " + file.toString());
167 }
168 return file;
169 }
170
152 public static class ConsoleProgressListener implements ProgressListener { 171 public static class ConsoleProgressListener implements ProgressListener {
153 172
154 private static final int ReportTime = 5000; // 5s 173 private static final int ReportTime = 5000; // 5s
@@ -166,7 +185,7 @@ public class CommandMain {
166 } 185 }
167 186
168 @Override 187 @Override
169 public void onProgress(int numDone, String message) { 188 public void step(int numDone, String message) {
170 long now = System.currentTimeMillis(); 189 long now = System.currentTimeMillis();
171 boolean isLastUpdate = numDone == this.totalWork; 190 boolean isLastUpdate = numDone == this.totalWork;
172 boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; 191 boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime;
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 4a945cdb..076c5468 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -13,8 +13,6 @@ package cuchaz.enigma;
13 13
14import com.google.common.base.Stopwatch; 14import com.google.common.base.Stopwatch;
15import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Sets;
18import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
19import com.strobel.assembler.metadata.MetadataSystem; 17import com.strobel.assembler.metadata.MetadataSystem;
20import com.strobel.assembler.metadata.TypeDefinition; 18import com.strobel.assembler.metadata.TypeDefinition;
@@ -28,16 +26,17 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit;
28import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 26import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
29import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 27import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
30import cuchaz.enigma.analysis.*; 28import cuchaz.enigma.analysis.*;
29import cuchaz.enigma.analysis.index.JarIndex;
31import cuchaz.enigma.api.EnigmaPlugin; 30import cuchaz.enigma.api.EnigmaPlugin;
32import cuchaz.enigma.mapping.*; 31import cuchaz.enigma.translation.mapping.*;
33import cuchaz.enigma.mapping.entry.*; 32import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
34import cuchaz.enigma.throwables.IllegalNameException; 33import cuchaz.enigma.translation.mapping.tree.EntryTree;
34import cuchaz.enigma.translation.representation.ReferencedEntryPool;
35import cuchaz.enigma.translation.representation.entry.ClassEntry;
36import cuchaz.enigma.translation.representation.entry.Entry;
37import cuchaz.enigma.translation.representation.entry.MethodEntry;
35import cuchaz.enigma.utils.Utils; 38import cuchaz.enigma.utils.Utils;
36import oml.ast.transformers.InvalidIdentifierFix; 39import oml.ast.transformers.*;
37import oml.ast.transformers.Java8Generics;
38import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform;
39import oml.ast.transformers.RemoveObjectCasts;
40import oml.ast.transformers.VarargsFixer;
41import org.objectweb.asm.ClassWriter; 40import org.objectweb.asm.ClassWriter;
42import org.objectweb.asm.tree.ClassNode; 41import org.objectweb.asm.tree.ClassNode;
43 42
@@ -49,6 +48,7 @@ import java.util.function.Consumer;
49import java.util.jar.JarEntry; 48import java.util.jar.JarEntry;
50import java.util.jar.JarFile; 49import java.util.jar.JarFile;
51import java.util.jar.JarOutputStream; 50import java.util.jar.JarOutputStream;
51import java.util.stream.Collectors;
52 52
53public class Deobfuscator { 53public class Deobfuscator {
54 54
@@ -57,24 +57,24 @@ public class Deobfuscator {
57 private final ParsedJar parsedJar; 57 private final ParsedJar parsedJar;
58 private final DecompilerSettings settings; 58 private final DecompilerSettings settings;
59 private final JarIndex jarIndex; 59 private final JarIndex jarIndex;
60 private final MappingsRenamer renamer; 60 private final IndexTreeBuilder indexTreeBuilder;
61 private final Map<TranslationDirection, Translator> translatorCache; 61 private EntryRemapper mapper;
62 private Mappings mappings;
63 62
64 public Deobfuscator(ParsedJar jar, Consumer<String> listener) { 63 public Deobfuscator(ParsedJar jar, Consumer<String> listener) {
65 this.parsedJar = jar; 64 this.parsedJar = jar;
66 65
67 // build the jar index 66 // build the jar index
68 listener.accept("Indexing JAR..."); 67 this.jarIndex = JarIndex.empty();
69 this.jarIndex = new JarIndex(entryPool); 68 this.jarIndex.indexJar(this.parsedJar, listener);
70 this.jarIndex.indexJar(this.parsedJar, true);
71 69
72 listener.accept("Initializing plugins..."); 70 listener.accept("Initializing plugins...");
73 for (EnigmaPlugin plugin : getPlugins()) { 71 for (EnigmaPlugin plugin : getPlugins()) {
74 plugin.onClassesLoaded(parsedJar.getClassDataMap(), parsedJar::getClassNode); 72 plugin.onClassesLoaded(parsedJar.getClassDataMap(), parsedJar::getClassNode);
75 } 73 }
76 74
77 listener.accept("Preparing..."); 75 this.indexTreeBuilder = new IndexTreeBuilder(jarIndex);
76
77 listener.accept("Preparing...");
78 // config the decompiler 78 // config the decompiler
79 this.settings = DecompilerSettings.javaDefaults(); 79 this.settings = DecompilerSettings.javaDefaults();
80 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 80 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
@@ -85,11 +85,8 @@ public class Deobfuscator {
85 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 85 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
86 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 86 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
87 87
88 // init defaults
89 this.translatorCache = Maps.newTreeMap();
90 this.renamer = new MappingsRenamer(this.jarIndex, null, this.entryPool);
91 // init mappings 88 // init mappings
92 setMappings(new Mappings()); 89 mapper = new EntryRemapper(jarIndex);
93 } 90 }
94 91
95 public Deobfuscator(JarFile jar, Consumer<String> listener) throws IOException { 92 public Deobfuscator(JarFile jar, Consumer<String> listener) throws IOException {
@@ -97,12 +94,14 @@ public class Deobfuscator {
97 } 94 }
98 95
99 public Deobfuscator(ParsedJar jar) throws IOException { 96 public Deobfuscator(ParsedJar jar) throws IOException {
100 this(jar, (msg) -> {}); 97 this(jar, (msg) -> {
101 } 98 });
99 }
102 100
103 public Deobfuscator(JarFile jar) throws IOException { 101 public Deobfuscator(JarFile jar) throws IOException {
104 this(jar, (msg) -> {}); 102 this(jar, (msg) -> {
105 } 103 });
104 }
106 105
107 public ServiceLoader<EnigmaPlugin> getPlugins() { 106 public ServiceLoader<EnigmaPlugin> getPlugins() {
108 return plugins; 107 return plugins;
@@ -116,56 +115,50 @@ public class Deobfuscator {
116 return this.jarIndex; 115 return this.jarIndex;
117 } 116 }
118 117
119 public Mappings getMappings() { 118 public IndexTreeBuilder getIndexTreeBuilder() {
120 return this.mappings; 119 return indexTreeBuilder;
121 } 120 }
122 121
123 public void setMappings(Mappings val) { 122 public EntryRemapper getMapper() {
124 setMappings(val, true); 123 return this.mapper;
125 } 124 }
126 125
127 public void setMappings(Mappings val, boolean warnAboutDrops) { 126 public void setMappings(EntryTree<EntryMapping> mappings) {
128 if (val == null) { 127 if (mappings != null) {
129 val = new Mappings(); 128 Collection<Entry<?>> dropped = dropMappings(mappings);
130 } 129 mapper = new EntryRemapper(jarIndex, mappings);
131 130
132 // drop mappings that don't match the jar 131 DeltaTrackingTree<EntryMapping> deobfToObf = mapper.getDeobfToObf();
133 MappingsChecker checker = new MappingsChecker(this.jarIndex); 132 for (Entry<?> entry : dropped) {
134 checker.dropBrokenMappings(val); 133 deobfToObf.trackDeletion(entry);
135 if (warnAboutDrops) {
136 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
137 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
138 }
139 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
140 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
141 }
142 for (Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
143 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
144 }
145 for (Map.Entry<MethodEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
146 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
147 } 134 }
135 } else {
136 mapper = new EntryRemapper(jarIndex);
148 } 137 }
149
150 this.mappings = val;
151 this.renamer.setMappings(mappings);
152 this.translatorCache.clear();
153 } 138 }
154 139
155 public Translator getTranslator(TranslationDirection direction) { 140 private Collection<Entry<?>> dropMappings(EntryTree<EntryMapping> mappings) {
156 return this.translatorCache.computeIfAbsent(direction, 141 // drop mappings that don't match the jar
157 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 142 MappingsChecker checker = new MappingsChecker(jarIndex, mappings);
143 MappingsChecker.Dropped dropped = checker.dropBrokenMappings();
144
145 Map<Entry<?>, String> droppedMappings = dropped.getDroppedMappings();
146 for (Map.Entry<Entry<?>, String> mapping : droppedMappings.entrySet()) {
147 System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped.");
148 }
149
150 return droppedMappings.keySet();
158 } 151 }
159 152
160 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 153 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
161 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 154 for (ClassEntry obfClassEntry : this.jarIndex.getEntryIndex().getClasses()) {
162 // skip inner classes 155 // skip inner classes
163 if (obfClassEntry.isInnerClass()) { 156 if (obfClassEntry.isInnerClass()) {
164 continue; 157 continue;
165 } 158 }
166 159
167 // separate the classes 160 // separate the classes
168 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); 161 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry);
169 if (!deobfClassEntry.equals(obfClassEntry)) { 162 if (!deobfClassEntry.equals(obfClassEntry)) {
170 // if the class has a mapping, clearly it's deobfuscated 163 // if the class has a mapping, clearly it's deobfuscated
171 deobfClasses.add(deobfClassEntry); 164 deobfClasses.add(deobfClassEntry);
@@ -184,8 +177,8 @@ public class Deobfuscator {
184 this.parsedJar, 177 this.parsedJar,
185 this.jarIndex, 178 this.jarIndex,
186 this.entryPool, 179 this.entryPool,
187 getTranslator(TranslationDirection.OBFUSCATING), 180 this.mapper.getObfuscator(),
188 getTranslator(TranslationDirection.DEOBFUSCATING) 181 this.mapper.getDeobfuscator()
189 ); 182 );
190 } 183 }
191 184
@@ -203,14 +196,7 @@ public class Deobfuscator {
203 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 196 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
204 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one 197 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
205 198
206 // first, assume class name is deobf 199 String deobfClassName = mapper.deobfuscate(new ClassEntry(className)).getFullName();
207 String deobfClassName = className;
208
209 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
210 ClassMapping classMapping = this.mappings.getClassByObf(className);
211 if (classMapping != null && classMapping.getDeobfName() != null) {
212 deobfClassName = classMapping.getDeobfName();
213 }
214 200
215 // set the desc loader 201 // set the desc loader
216 this.settings.setTypeLoader(loader); 202 this.settings.setTypeLoader(loader);
@@ -236,43 +222,23 @@ public class Deobfuscator {
236 } 222 }
237 223
238 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 224 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
239 return getSourceIndex(sourceTree, source, null); 225 return getSourceIndex(sourceTree, source, true);
240 } 226 }
241 227
242 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { 228 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, boolean ignoreBadTokens) {
243 229
244 // build the source index 230 // build the source index
245 SourceIndex index; 231 SourceIndex index = new SourceIndex(source, ignoreBadTokens);
246 if (ignoreBadTokens != null) {
247 index = new SourceIndex(source, ignoreBadTokens);
248 } else {
249 index = new SourceIndex(source);
250 }
251 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index); 232 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index);
252 233
253 // DEBUG 234 EntryResolver resolver = mapper.getDeobfResolver();
254 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
255
256 // resolve all the classes in the source references
257 for (Token token : index.referenceTokens()) {
258 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token);
259
260 // get the obfuscated entry
261 Entry obfEntry = obfuscateEntry(deobfReference.entry);
262 235
263 // try to resolve the class 236 Collection<Token> tokens = Lists.newArrayList(index.referenceTokens());
264 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryOwner(obfEntry);
265 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getOwnerClassEntry())) {
266 // change the class of the entry
267 obfEntry = obfEntry.updateOwnership(resolvedObfClassEntry);
268
269 // save the new deobfuscated reference
270 deobfReference.entry = deobfuscateEntry(obfEntry);
271 index.replaceDeobfReference(token, deobfReference);
272 }
273 237
274 // DEBUG 238 // resolve all the classes in the source references
275 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); 239 for (Token token : tokens) {
240 EntryReference<Entry<?>, Entry<?>> deobfReference = index.getDeobfReference(token);
241 index.replaceDeobfReference(token, resolver.resolveFirstReference(deobfReference, ResolutionStrategy.RESOLVE_CLOSEST));
276 } 242 }
277 243
278 return index; 244 return index;
@@ -288,15 +254,9 @@ public class Deobfuscator {
288 254
289 public void writeSources(File dirOut, ProgressListener progress) { 255 public void writeSources(File dirOut, ProgressListener progress) {
290 // get the classes to decompile 256 // get the classes to decompile
291 Set<ClassEntry> classEntries = Sets.newHashSet(); 257 Set<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses().stream()
292 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 258 .filter(classEntry -> !classEntry.isInnerClass())
293 // skip inner classes 259 .collect(Collectors.toSet());
294 if (obfClassEntry.isInnerClass()) {
295 continue;
296 }
297
298 classEntries.add(obfClassEntry);
299 }
300 260
301 if (progress != null) { 261 if (progress != null) {
302 progress.init(classEntries.size(), "Decompiling classes..."); 262 progress.init(classEntries.size(), "Decompiling classes...");
@@ -313,9 +273,9 @@ public class Deobfuscator {
313 Stopwatch stopwatch = Stopwatch.createStarted(); 273 Stopwatch stopwatch = Stopwatch.createStarted();
314 AtomicInteger count = new AtomicInteger(); 274 AtomicInteger count = new AtomicInteger();
315 classEntries.parallelStream().forEach(obfClassEntry -> { 275 classEntries.parallelStream().forEach(obfClassEntry -> {
316 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); 276 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry);
317 if (progress != null) { 277 if (progress != null) {
318 progress.onProgress(count.getAndIncrement(), deobfClassEntry.toString()); 278 progress.step(count.getAndIncrement(), deobfClassEntry.toString());
319 } 279 }
320 280
321 try { 281 try {
@@ -332,131 +292,17 @@ public class Deobfuscator {
332 } catch (Throwable t) { 292 } catch (Throwable t) {
333 // don't crash the whole world here, just log the error and keep going 293 // don't crash the whole world here, just log the error and keep going
334 // TODO: set up logback via log4j 294 // TODO: set up logback via log4j
335 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")"); 295 System.err.println("Unable to decompile class " + deobfClassEntry + " (" + obfClassEntry + ")");
336 t.printStackTrace(System.err); 296 t.printStackTrace(System.err);
337 } 297 }
338 }); 298 });
339 stopwatch.stop(); 299 stopwatch.stop();
340 System.out.println("writeSources Done in : " + stopwatch.toString()); 300 System.out.println("writeSources Done in : " + stopwatch.toString());
341 if (progress != null) { 301 if (progress != null) {
342 progress.onProgress(count.get(), "Done:"); 302 progress.step(count.get(), "Done:");
343 }
344 }
345
346 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) {
347 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) {
348 if (classEntries.add(interfaceEntry)) {
349 addAllPotentialAncestors(classEntries, interfaceEntry);
350 }
351 }
352
353 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry);
354 if (superClassEntry != null && classEntries.add(superClassEntry)) {
355 addAllPotentialAncestors(classEntries, superClassEntry);
356 }
357 }
358
359 public boolean isMethodProvider(MethodEntry methodEntry) {
360 Set<ClassEntry> classEntries = new HashSet<>();
361 addAllPotentialAncestors(classEntries, methodEntry.getOwnerClassEntry());
362
363 for (ClassEntry parentEntry : classEntries) {
364 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
365 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
366 return false;
367 }
368 }
369
370 return true;
371 }
372
373 @Deprecated
374 public boolean isMethodProvider(ClassEntry classObfEntry, MethodEntry methodEntry) {
375 Set<ClassEntry> classEntries = new HashSet<>();
376 addAllPotentialAncestors(classEntries, classObfEntry);
377
378 for (ClassEntry parentEntry : classEntries) {
379 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
380 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
381 return false;
382 }
383 }
384
385 return true;
386 }
387
388 public void rebuildMethodNames(ProgressListener progress) {
389 final AtomicInteger i = new AtomicInteger();
390 Map<ClassMapping, Map<Entry, String>> renameClassMap = new ConcurrentHashMap<>();
391
392 progress.init(getMappings().classes().size() * 3, "Rebuilding method names");
393
394 Lists.newArrayList(getMappings().classes()).parallelStream().forEach(classMapping -> {
395 progress.onProgress(i.getAndIncrement(), classMapping.getDeobfName());
396 rebuildMethodNames(classMapping, renameClassMap);
397 });
398
399
400 renameClassMap.entrySet().stream().forEach(renameClassMapEntry -> {
401 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
402 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
403 Entry obfEntry = entry.getKey();
404
405 removeMapping(obfEntry, false);
406 }
407 });
408
409 translatorCache.clear();
410
411 renameClassMap.entrySet().stream().forEach(renameClassMapEntry -> {
412 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
413
414 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
415 Entry obfEntry = entry.getKey();
416 String name = entry.getValue();
417
418 if (name != null) {
419 try {
420 rename(obfEntry, name);
421 } catch (IllegalNameException exception) {
422 System.out.println("WARNING: " + exception.getMessage());
423 }
424 }
425 }
426 });
427 }
428
429 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) {
430 Map<Entry, String> renameEntries = new HashMap<>();
431
432 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
433 ClassEntry classObfEntry = classMapping.getObfEntry();
434 MethodEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
435 boolean isProvider = isMethodProvider(obfEntry);
436
437 if (hasDeobfuscatedName(obfEntry)
438 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
439 renameEntries.put(obfEntry, isProvider ? methodMapping.getDeobfName() : null);
440 }
441
442 if (isProvider) {
443 for (LocalVariableMapping localVariableMapping : methodMapping.arguments()) {
444 Entry argObfEntry = localVariableMapping.getObfEntry(obfEntry);
445 if (hasDeobfuscatedName(argObfEntry)) {
446 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
447 }
448 }
449 }
450 }
451
452 classMapping.markDirty();
453 renameClassMap.put(classMapping, renameEntries);
454 for (ClassMapping innerClass : classMapping.innerClasses()) {
455 rebuildMethodNames(innerClass, renameClassMap);
456 } 303 }
457 } 304 }
458 305
459
460 public void writeJar(File out, ProgressListener progress) { 306 public void writeJar(File out, ProgressListener progress) {
461 transformJar(out, progress, createTypeLoader()::transformInto); 307 transformJar(out, progress, createTypeLoader()::transformInto);
462 } 308 }
@@ -470,7 +316,7 @@ public class Deobfuscator {
470 AtomicInteger i = new AtomicInteger(); 316 AtomicInteger i = new AtomicInteger();
471 parsedJar.visitNode(node -> { 317 parsedJar.visitNode(node -> {
472 if (progress != null) { 318 if (progress != null) {
473 progress.onProgress(i.getAndIncrement(), node.name); 319 progress.step(i.getAndIncrement(), node.name);
474 } 320 }
475 321
476 try { 322 try {
@@ -485,50 +331,31 @@ public class Deobfuscator {
485 }); 331 });
486 332
487 if (progress != null) { 333 if (progress != null) {
488 progress.onProgress(i.get(), "Done!"); 334 progress.step(i.get(), "Done!");
489 } 335 }
490 } catch (IOException ex) { 336 } catch (IOException ex) {
491 throw new Error("Unable to write to Jar file!"); 337 throw new Error("Unable to write to Jar file!");
492 } 338 }
493 } 339 }
494 340
495 public <T extends Entry> T obfuscateEntry(T deobfEntry) { 341 public AccessModifier getModifier(Entry<?> entry) {
496 if (deobfEntry == null) { 342 EntryMapping mapping = mapper.getDeobfMapping(entry);
497 return null; 343 if (mapping == null) {
498 } 344 return AccessModifier.UNCHANGED;
499 T translatedEntry = getTranslator(TranslationDirection.OBFUSCATING).getTranslatedEntry(deobfEntry);
500 if (translatedEntry == null) {
501 return deobfEntry;
502 }
503 return translatedEntry;
504 }
505
506 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
507 if (obfEntry == null) {
508 return null;
509 }
510 T translatedEntry = getTranslator(TranslationDirection.DEOBFUSCATING).getTranslatedEntry(obfEntry);
511 if (translatedEntry == null) {
512 return obfEntry;
513 }
514 return translatedEntry;
515 }
516
517 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
518 if (deobfReference == null) {
519 return null;
520 } 345 }
521 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); 346 return mapping.getAccessModifier();
522 } 347 }
523 348
524 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { 349 public void changeModifier(Entry<?> entry, AccessModifier modifier) {
525 if (obfReference == null) { 350 EntryMapping mapping = mapper.getDeobfMapping(entry);
526 return null; 351 if (mapping != null) {
352 mapper.mapFromObf(entry, new EntryMapping(mapping.getTargetName(), modifier));
353 } else {
354 mapper.mapFromObf(entry, new EntryMapping(entry.getName(), modifier));
527 } 355 }
528 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference);
529 } 356 }
530 357
531 public boolean isObfuscatedIdentifier(Entry obfEntry) { 358 public boolean isObfuscatedIdentifier(Entry<?> obfEntry) {
532 if (obfEntry instanceof MethodEntry) { 359 if (obfEntry instanceof MethodEntry) {
533 // HACKHACK: Object methods are not obfuscated identifiers 360 // HACKHACK: Object methods are not obfuscated identifiers
534 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 361 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
@@ -559,142 +386,30 @@ public class Deobfuscator {
559 } 386 }
560 } 387 }
561 388
562 return this.jarIndex.containsObfEntry(obfEntry); 389 return this.jarIndex.getEntryIndex().hasEntry(obfEntry);
563 } 390 }
564 391
565 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { 392 public boolean isRenameable(EntryReference<Entry<?>, Entry<?>> obfReference) {
566 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); 393 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry());
567 } 394 }
568 395
569 public boolean hasDeobfuscatedName(Entry obfEntry) { 396 public boolean hasDeobfuscatedName(Entry<?> obfEntry) {
570 Translator translator = getTranslator(TranslationDirection.DEOBFUSCATING); 397 return mapper.hasDeobfMapping(obfEntry);
571 if (obfEntry instanceof ClassEntry) {
572 ClassEntry obfClass = (ClassEntry) obfEntry;
573 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
574 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
575 return classMapping != null && classMapping.getDeobfName() != null;
576 } else if (obfEntry instanceof FieldEntry) {
577 return translator.hasFieldMapping((FieldEntry) obfEntry);
578 } else if (obfEntry instanceof MethodEntry) {
579 MethodEntry methodEntry = (MethodEntry) obfEntry;
580 if (methodEntry.isConstructor()) {
581 return false;
582 }
583 return translator.hasMethodMapping(methodEntry);
584 } else if (obfEntry instanceof LocalVariableEntry) {
585 return translator.hasLocalVariableMapping((LocalVariableEntry) obfEntry);
586 } else {
587 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
588 }
589 } 398 }
590 399
591 public void rename(Entry obfEntry, String newName) { 400 public void rename(Entry<?> obfEntry, String newName) {
592 rename(obfEntry, newName, true); 401 mapper.mapFromObf(obfEntry, new EntryMapping(newName));
593 } 402 }
594 403
595 // NOTE: these methods are a bit messy... oh well 404 public void removeMapping(Entry<?> obfEntry) {
596 405 mapper.removeByObf(obfEntry);
597 public void rename(Entry obfEntry, String newName, boolean clearCache) {
598 if (obfEntry instanceof ClassEntry) {
599 this.renamer.setClassName((ClassEntry) obfEntry, newName);
600 } else if (obfEntry instanceof FieldEntry) {
601 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
602 } else if (obfEntry instanceof MethodEntry) {
603 if (((MethodEntry) obfEntry).isConstructor()) {
604 throw new IllegalArgumentException("Cannot rename constructors");
605 }
606
607 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
608 } else if (obfEntry instanceof LocalVariableEntry) {
609 // TODO: Discern between arguments (propagate) and local vars (don't)
610 this.renamer.setLocalVariableTreeName((LocalVariableEntry) obfEntry, newName);
611 } else {
612 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
613 }
614
615 // clear caches
616 if (clearCache)
617 this.translatorCache.clear();
618 } 406 }
619 407
620 public void removeMapping(Entry obfEntry) { 408 public void markAsDeobfuscated(Entry<?> obfEntry) {
621 removeMapping(obfEntry, true); 409 mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName()));
622 } 410 }
623 411
624 public void removeMapping(Entry obfEntry, boolean clearCache) { 412 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context) {
625 if (obfEntry instanceof ClassEntry) {
626 this.renamer.removeClassMapping((ClassEntry) obfEntry);
627 } else if (obfEntry instanceof FieldEntry) {
628 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
629 } else if (obfEntry instanceof MethodEntry) {
630 if (((MethodEntry) obfEntry).isConstructor()) {
631 throw new IllegalArgumentException("Cannot rename constructors");
632 }
633 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
634 } else if (obfEntry instanceof LocalVariableEntry) {
635 this.renamer.removeLocalVariableMapping((LocalVariableEntry) obfEntry);
636 } else {
637 throw new Error("Unknown entry desc: " + obfEntry);
638 }
639
640 // clear caches
641 if (clearCache)
642 this.translatorCache.clear();
643 }
644
645 public void markAsDeobfuscated(Entry obfEntry) {
646 markAsDeobfuscated(obfEntry, true);
647 }
648
649 public void markAsDeobfuscated(Entry obfEntry, boolean clearCache) {
650 if (obfEntry instanceof ClassEntry) {
651 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry);
652 } else if (obfEntry instanceof FieldEntry) {
653 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
654 } else if (obfEntry instanceof MethodEntry) {
655 MethodEntry methodEntry = (MethodEntry) obfEntry;
656 if (methodEntry.isConstructor()) {
657 throw new IllegalArgumentException("Cannot rename constructors");
658 }
659 this.renamer.markMethodTreeAsDeobfuscated(methodEntry);
660 } else if (obfEntry instanceof LocalVariableEntry) {
661 this.renamer.markArgumentAsDeobfuscated((LocalVariableEntry) obfEntry);
662 } else {
663 throw new Error("Unknown entry desc: " + obfEntry);
664 }
665
666 // clear caches
667 if (clearCache)
668 this.translatorCache.clear();
669 }
670
671 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) {
672 Entry obfEntry = obfuscateEntry(entry);
673 if (obfEntry instanceof ClassEntry)
674 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
675 else if (obfEntry instanceof FieldEntry)
676 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
677 else if (obfEntry instanceof MethodEntry)
678 this.renamer.setMethodModifier((MethodEntry) obfEntry, modifierEntry);
679 else
680 throw new Error("Unknown entry desc: " + obfEntry);
681 }
682
683 public Mappings.EntryModifier getModifier(Entry obfEntry) {
684 Entry entry = obfuscateEntry(obfEntry);
685 if (entry != null)
686 obfEntry = entry;
687 if (obfEntry instanceof ClassEntry)
688 return this.renamer.getClassModifier((ClassEntry) obfEntry);
689 else if (obfEntry instanceof FieldEntry)
690 return this.renamer.getFieldModifier((FieldEntry) obfEntry);
691 else if (obfEntry instanceof MethodEntry)
692 return this.renamer.getMethodModfifier((MethodEntry) obfEntry);
693 else
694 throw new Error("Unknown entry desc: " + obfEntry);
695 }
696
697 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){
698 List<IAstTransform> transformers = Arrays.asList( 413 List<IAstTransform> transformers = Arrays.asList(
699 new ObfuscatedEnumSwitchRewriterTransform(context), 414 new ObfuscatedEnumSwitchRewriterTransform(context),
700 new VarargsFixer(context), 415 new VarargsFixer(context),
@@ -707,12 +422,6 @@ public class Deobfuscator {
707 } 422 }
708 } 423 }
709 424
710 public interface ProgressListener {
711 void init(int totalWork, String title);
712
713 void onProgress(int numDone, String message);
714 }
715
716 public interface ClassTransformer { 425 public interface ClassTransformer {
717 String transform(ClassNode node, ClassWriter writer); 426 String transform(ClassNode node, ClassWriter writer);
718 } 427 }
diff --git a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
index 547ed0b2..24822dd3 100644
--- a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java
@@ -1,7 +1,7 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import com.strobel.assembler.metadata.ITypeLoader; 3import com.strobel.assembler.metadata.ITypeLoader;
4import cuchaz.enigma.mapping.entry.ClassEntry; 4import cuchaz.enigma.translation.representation.entry.ClassEntry;
5import org.objectweb.asm.ClassWriter; 5import org.objectweb.asm.ClassWriter;
6import org.objectweb.asm.tree.ClassNode; 6import org.objectweb.asm.tree.ClassNode;
7 7
diff --git a/src/main/java/cuchaz/enigma/Main.java b/src/main/java/cuchaz/enigma/Main.java
index 0f151934..ccfc51f0 100644
--- a/src/main/java/cuchaz/enigma/Main.java
+++ b/src/main/java/cuchaz/enigma/Main.java
@@ -11,12 +11,12 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.config.Config;
15import cuchaz.enigma.config.Themes;
16import cuchaz.enigma.gui.Gui; 14import cuchaz.enigma.gui.Gui;
15import cuchaz.enigma.translation.mapping.serde.MappingFormat;
17 16
18import javax.swing.*;
19import java.io.File; 17import java.io.File;
18import java.nio.file.Files;
19import java.nio.file.Path;
20import java.util.jar.JarFile; 20import java.util.jar.JarFile;
21 21
22public class Main { 22public class Main {
@@ -29,7 +29,12 @@ public class Main {
29 gui.getController().openJar(new JarFile(getFile(args[0]))); 29 gui.getController().openJar(new JarFile(getFile(args[0])));
30 } 30 }
31 if (args.length >= 2) { 31 if (args.length >= 2) {
32 gui.getController().openEnigmaMappings(getFile(args[1])); 32 Path mappingsFile = getFile(args[1]).toPath();
33 if (Files.isDirectory(mappingsFile)) {
34 gui.getController().openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsFile);
35 } else {
36 gui.getController().openMappings(MappingFormat.ENIGMA_FILE, mappingsFile);
37 }
33 } 38 }
34 39
35 // DEBUG 40 // DEBUG
diff --git a/src/main/java/cuchaz/enigma/ProgressListener.java b/src/main/java/cuchaz/enigma/ProgressListener.java
new file mode 100644
index 00000000..ffce297d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/ProgressListener.java
@@ -0,0 +1,17 @@
1package cuchaz.enigma;
2
3public interface ProgressListener {
4 ProgressListener VOID = new ProgressListener() {
5 @Override
6 public void init(int totalWork, String title) {
7 }
8
9 @Override
10 public void step(int numDone, String message) {
11 }
12 };
13
14 void init(int totalWork, String title);
15
16 void step(int numDone, String message);
17}
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
index f4a7fe09..657bee42 100644
--- a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java
@@ -1,7 +1,7 @@
1package cuchaz.enigma; 1package cuchaz.enigma;
2 2
3import com.strobel.assembler.metadata.Buffer; 3import com.strobel.assembler.metadata.Buffer;
4import cuchaz.enigma.mapping.entry.ClassEntry; 4import cuchaz.enigma.translation.representation.entry.ClassEntry;
5import org.objectweb.asm.ClassWriter; 5import org.objectweb.asm.ClassWriter;
6import org.objectweb.asm.tree.ClassNode; 6import org.objectweb.asm.tree.ClassNode;
7 7
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 42ceec4d..4c1f695b 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -14,12 +14,12 @@ 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.JarIndex; 17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.analysis.ParsedJar; 18import cuchaz.enigma.analysis.ParsedJar;
19import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; 19import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
20import cuchaz.enigma.mapping.entry.ClassEntry; 20import cuchaz.enigma.translation.Translator;
21import cuchaz.enigma.mapping.entry.ReferencedEntryPool; 21import cuchaz.enigma.translation.representation.ReferencedEntryPool;
22import cuchaz.enigma.mapping.Translator; 22import cuchaz.enigma.translation.representation.entry.ClassEntry;
23import org.objectweb.asm.ClassWriter; 23import org.objectweb.asm.ClassWriter;
24import org.objectweb.asm.Opcodes; 24import org.objectweb.asm.Opcodes;
25import org.objectweb.asm.tree.AbstractInsnNode; 25import org.objectweb.asm.tree.AbstractInsnNode;
@@ -47,12 +47,12 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
47 this.deobfuscatingTranslator = deobfuscatingTranslator; 47 this.deobfuscatingTranslator = deobfuscatingTranslator;
48 } 48 }
49 49
50 protected byte[] doLoad(String className){ 50 protected byte[] doLoad(String className) {
51 byte[] data = loadType(className); 51 byte[] data = loadType(className);
52 if (data == null) { 52 if (data == null) {
53 // chain to default desc loader 53 // chain to default desc loader
54 Buffer parentBuf = new Buffer(); 54 Buffer parentBuf = new Buffer();
55 if (defaultTypeLoader.tryLoadType(className, parentBuf)){ 55 if (defaultTypeLoader.tryLoadType(className, parentBuf)) {
56 return parentBuf.array(); 56 return parentBuf.array();
57 } 57 }
58 return EMPTY_ARRAY;//need to return *something* as null means no store 58 return EMPTY_ARRAY;//need to return *something* as null means no store
@@ -64,21 +64,10 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
64 64
65 // NOTE: don't know if class name is obf or deobf 65 // NOTE: don't know if class name is obf or deobf
66 ClassEntry classEntry = new ClassEntry(className); 66 ClassEntry classEntry = new ClassEntry(className);
67 ClassEntry obfClassEntry = this.obfuscatingTranslator.getTranslatedClass(classEntry); 67 ClassEntry obfClassEntry = this.obfuscatingTranslator.translate(classEntry);
68
69 // is this an inner class referenced directly? (ie trying to load b instead of a$b)
70 if (!obfClassEntry.isInnerClass()) {
71 List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry);
72 if (classChain.size() > 1) {
73 System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s",
74 className, obfClassEntry.buildClassEntry(classChain)
75 ));
76 return null;
77 }
78 }
79 68
80 // is this a class we should even know about? 69 // is this a class we should even know about?
81 if (!this.jarIndex.containsObfClass(obfClassEntry)) { 70 if (!jarIndex.getEntryIndex().hasClass(obfClassEntry)) {
82 return null; 71 return null;
83 } 72 }
84 73
@@ -97,15 +86,15 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
97 // DUP 86 // DUP
98 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; 87 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
99 // POP 88 // POP
100 for (MethodNode methodNode : node.methods){ 89 for (MethodNode methodNode : node.methods) {
101 AbstractInsnNode insnNode = methodNode.instructions.getFirst(); 90 AbstractInsnNode insnNode = methodNode.instructions.getFirst();
102 while (insnNode != null){ 91 while (insnNode != null) {
103 if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL){ 92 if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
104 MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; 93 MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
105 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")){ 94 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) {
106 AbstractInsnNode previous = methodInsnNode.getPrevious(); 95 AbstractInsnNode previous = methodInsnNode.getPrevious();
107 AbstractInsnNode next = methodInsnNode.getNext(); 96 AbstractInsnNode next = methodInsnNode.getNext();
108 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP){ 97 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) {
109 insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction 98 insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction
110 methodNode.instructions.remove(previous); 99 methodNode.instructions.remove(previous);
111 methodNode.instructions.remove(methodInsnNode); 100 methodNode.instructions.remove(methodInsnNode);
@@ -140,24 +129,26 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
140 129
141 @Override 130 @Override
142 public List<String> getClassNamesToTry(String className) { 131 public List<String> getClassNamesToTry(String className) {
143 return getClassNamesToTry(this.obfuscatingTranslator.getTranslatedClass(new ClassEntry(className))); 132 return getClassNamesToTry(this.obfuscatingTranslator.translate(new ClassEntry(className)));
144 } 133 }
145 134
146 @Override 135 @Override
147 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 136 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
148 List<String> classNamesToTry = Lists.newArrayList(); 137 List<String> classNamesToTry = Lists.newArrayList();
149 classNamesToTry.add(obfClassEntry.getName()); 138 classNamesToTry.add(obfClassEntry.getFullName());
150 if (obfClassEntry.isInnerClass()) { 139
151 // try just the inner class name 140 ClassEntry outerClass = obfClassEntry.getOuterClass();
152 classNamesToTry.add(obfClassEntry.getInnermostClassName()); 141 if (outerClass != null) {
142 classNamesToTry.addAll(getClassNamesToTry(outerClass));
153 } 143 }
144
154 return classNamesToTry; 145 return classNamesToTry;
155 } 146 }
156 147
157 @Override 148 @Override
158 public String transformInto(ClassNode node, ClassWriter writer) { 149 public String transformInto(ClassNode node, ClassWriter writer) {
159 node.accept(new TranslationClassVisitor(deobfuscatingTranslator, jarIndex, entryPool, Opcodes.ASM5, writer)); 150 node.accept(new TranslationClassVisitor(deobfuscatingTranslator, entryPool, Opcodes.ASM5, writer));
160 return deobfuscatingTranslator.getTranslatedClass(new ClassEntry(node.name)).getName(); 151 return deobfuscatingTranslator.translate(new ClassEntry(node.name)).getFullName();
161 } 152 }
162 153
163} 154}
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java
index 81814183..82ca6692 100644
--- a/src/main/java/cuchaz/enigma/analysis/Access.java
+++ b/src/main/java/cuchaz/enigma/analysis/Access.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.bytecode.AccessFlags; 14import cuchaz.enigma.translation.representation.AccessFlags;
15 15
16import java.lang.reflect.Modifier; 16import java.lang.reflect.Modifier;
17 17
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
index e876bb07..0fc44ca6 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java
@@ -12,26 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 20
19import javax.swing.tree.DefaultMutableTreeNode; 21import javax.swing.tree.DefaultMutableTreeNode;
22import java.util.Collection;
20import java.util.List; 23import java.util.List;
21 24
22public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { 25public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
23 26 private final Translator translator;
24 private final Translator deobfuscatingTranslator;
25 private final ClassEntry entry; 27 private final ClassEntry entry;
26 28
27 public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 29 public ClassImplementationsTreeNode(Translator translator, ClassEntry entry) {
28 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.translator = translator;
29 this.entry = entry; 31 this.entry = entry;
30 } 32 }
31 33
32 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { 34 public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) {
33 // is this the node? 35 // is this the node?
34 if (node.entry.equals(entry.getOwnerClassEntry())) { 36 if (node.entry.equals(entry.getParent())) {
35 return node; 37 return node;
36 } 38 }
37 39
@@ -49,24 +51,19 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode {
49 return this.entry; 51 return this.entry;
50 } 52 }
51 53
52 public String getDeobfClassName() {
53 return this.deobfuscatingTranslator.getTranslatedClass(entry).getClassName();
54 }
55
56 @Override 54 @Override
57 public String toString() { 55 public String toString() {
58 String className = getDeobfClassName(); 56 return translator.translate(entry).toString();
59 if (className == null) {
60 className = this.entry.getClassName();
61 }
62 return className;
63 } 57 }
64 58
65 public void load(JarIndex index) { 59 public void load(JarIndex index) {
66 // get all method implementations 60 // get all method implementations
67 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); 61 List<ClassImplementationsTreeNode> nodes = Lists.newArrayList();
68 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 62 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
69 nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); 63
64 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry);
65 for (ClassEntry inheritor : inheritors) {
66 nodes.add(new ClassImplementationsTreeNode(translator, inheritor));
70 } 67 }
71 68
72 // add them to this node 69 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index b8ee17da..7904c5f0 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -12,25 +12,25 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.InheritanceIndex;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
17 18
18import javax.swing.tree.DefaultMutableTreeNode; 19import javax.swing.tree.DefaultMutableTreeNode;
19import java.util.List; 20import java.util.List;
20 21
21public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { 22public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
22 23 private final Translator translator;
23 private final Translator deobfuscatingTranslator;
24 private final ClassEntry obfClassEntry; 24 private final ClassEntry obfClassEntry;
25 25
26 public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { 26 public ClassInheritanceTreeNode(Translator translator, String obfClassName) {
27 this.deobfuscatingTranslator = deobfuscatingTranslator; 27 this.translator = translator;
28 this.obfClassEntry = new ClassEntry(obfClassName); 28 this.obfClassEntry = new ClassEntry(obfClassName);
29 } 29 }
30 30
31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { 31 public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) {
32 // is this the node? 32 // is this the node?
33 if (node.getObfClassName().equals(entry.getName())) { 33 if (node.getObfClassName().equals(entry.getFullName())) {
34 return node; 34 return node;
35 } 35 }
36 36
@@ -45,27 +45,19 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
45 } 45 }
46 46
47 public String getObfClassName() { 47 public String getObfClassName() {
48 return this.obfClassEntry.getClassName(); 48 return this.obfClassEntry.getFullName();
49 }
50
51 public String getDeobfClassName() {
52 return this.deobfuscatingTranslator.getTranslatedClass(this.obfClassEntry).getClassName();
53 } 49 }
54 50
55 @Override 51 @Override
56 public String toString() { 52 public String toString() {
57 String deobfClassName = getDeobfClassName(); 53 return translator.translate(obfClassEntry).getFullName();
58 if (deobfClassName != null) {
59 return deobfClassName;
60 }
61 return this.obfClassEntry.getName();
62 } 54 }
63 55
64 public void load(TranslationIndex ancestries, boolean recurse) { 56 public void load(InheritanceIndex ancestries, boolean recurse) {
65 // get all the child nodes 57 // get all the child nodes
66 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 58 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
67 for (ClassEntry subclassEntry : ancestries.getSubclass(this.obfClassEntry)) { 59 for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) {
68 nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); 60 nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName()));
69 } 61 }
70 62
71 // add them to this node 63 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
index ff5f2e9a..90d8a6cf 100644
--- a/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java
@@ -12,12 +12,12 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.mapping.Translator; 16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.mapping.entry.Entry; 18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.mapping.entry.MethodDefEntry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.mapping.entry.MethodEntry; 20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21 21
22import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
23import javax.swing.tree.TreeNode; 23import javax.swing.tree.TreeNode;
@@ -29,7 +29,6 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
29 private Translator deobfuscatingTranslator; 29 private Translator deobfuscatingTranslator;
30 private ClassEntry entry; 30 private ClassEntry entry;
31 private EntryReference<ClassEntry, MethodDefEntry> reference; 31 private EntryReference<ClassEntry, MethodDefEntry> reference;
32 private AccessFlags access;
33 32
34 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { 33 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) {
35 this.deobfuscatingTranslator = deobfuscatingTranslator; 34 this.deobfuscatingTranslator = deobfuscatingTranslator;
@@ -37,12 +36,10 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
37 this.reference = null; 36 this.reference = null;
38 } 37 }
39 38
40 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, 39 public ClassReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<ClassEntry, MethodDefEntry> reference) {
41 EntryReference<ClassEntry, MethodDefEntry> reference, AccessFlags access) {
42 this.deobfuscatingTranslator = deobfuscatingTranslator; 40 this.deobfuscatingTranslator = deobfuscatingTranslator;
43 this.entry = reference.entry; 41 this.entry = reference.entry;
44 this.reference = reference; 42 this.reference = reference;
45 this.access = access;
46 } 43 }
47 44
48 @Override 45 @Override
@@ -58,16 +55,17 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
58 @Override 55 @Override
59 public String toString() { 56 public String toString() {
60 if (this.reference != null) { 57 if (this.reference != null) {
61 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), 58 return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context));
62 this.access);
63 } 59 }
64 return this.deobfuscatingTranslator.getTranslatedClass(this.entry).getName(); 60 return this.deobfuscatingTranslator.translate(this.entry).getFullName();
65 } 61 }
66 62
67 public void load(JarIndex index, boolean recurse) { 63 public void load(JarIndex index, boolean recurse) {
64 ReferenceIndex referenceIndex = index.getReferenceIndex();
65
68 // get all the child nodes 66 // get all the child nodes
69 for (EntryReference<ClassEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry)) { 67 for (EntryReference<ClassEntry, MethodDefEntry> reference : referenceIndex.getReferencesToClass(this.entry)) {
70 add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 68 add(new ClassReferenceTreeNode(this.deobfuscatingTranslator, reference));
71 } 69 }
72 70
73 if (recurse && this.children != null) { 71 if (recurse && this.children != null) {
@@ -76,7 +74,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode
76 ClassReferenceTreeNode node = (ClassReferenceTreeNode) child; 74 ClassReferenceTreeNode node = (ClassReferenceTreeNode) child;
77 75
78 // don't recurse into ancestor 76 // don't recurse into ancestor
79 Set<Entry> ancestors = Sets.newHashSet(); 77 Set<Entry<?>> ancestors = Sets.newHashSet();
80 TreeNode n = node; 78 TreeNode n = node;
81 while (n.getParent() != null) { 79 while (n.getParent() != null) {
82 n = n.getParent(); 80 n = n.getParent();
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
index df36c236..e122210c 100644
--- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java
+++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java
@@ -11,15 +11,20 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry; 14import cuchaz.enigma.translation.Translatable;
15import cuchaz.enigma.mapping.entry.Entry; 15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.mapping.EntryResolver;
18import cuchaz.enigma.translation.mapping.EntryMap;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.Entry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import cuchaz.enigma.utils.Utils; 22import cuchaz.enigma.utils.Utils;
18 23
19import java.util.Arrays; 24import java.util.Arrays;
20import java.util.List; 25import java.util.List;
21 26
22public class EntryReference<E extends Entry, C extends Entry> { 27public class EntryReference<E extends Entry<?>, C extends Entry<?>> implements Translatable {
23 28
24 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); 29 private static final List<String> CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static");
25 public E entry; 30 public E entry;
@@ -53,32 +58,24 @@ public class EntryReference<E extends Entry, C extends Entry> {
53 58
54 public ClassEntry getLocationClassEntry() { 59 public ClassEntry getLocationClassEntry() {
55 if (context != null) { 60 if (context != null) {
56 return context.getOwnerClassEntry(); 61 return context.getContainingClass();
57 } 62 }
58 return entry.getOwnerClassEntry(); 63 return entry.getContainingClass();
59 } 64 }
60 65
61 public boolean isNamed() { 66 public boolean isNamed() {
62 return this.sourceName; 67 return this.sourceName;
63 } 68 }
64 69
65 public Entry getNameableEntry() { 70 public Entry<?> getNameableEntry() {
66 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) { 71 if (entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor()) {
67 // renaming a constructor really means renaming the class 72 // renaming a constructor really means renaming the class
68 return entry.getOwnerClassEntry(); 73 return entry.getContainingClass();
69 } 74 }
70 return entry; 75 return entry;
71 } 76 }
72 77
73 public String getNameableName() { 78 public String getNameableName() {
74 if (getNameableEntry() instanceof ClassEntry) {
75 ClassEntry classEntry = (ClassEntry) getNameableEntry();
76 if (classEntry.isInnerClass()) {
77 // make sure we only rename the inner class name
78 return classEntry.getInnermostClassName();
79 }
80 }
81
82 return getNameableEntry().getName(); 79 return getNameableEntry().getName();
83 } 80 }
84 81
@@ -121,4 +118,9 @@ public class EntryReference<E extends Entry, C extends Entry> {
121 } 118 }
122 return buf.toString(); 119 return buf.toString();
123 } 120 }
121
122 @Override
123 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
124 return new EntryReference<>(translator.translate(entry), translator.translate(context), this);
125 }
124} 126}
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
deleted file mode 100644
index c474d689..00000000
--- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java
+++ /dev/null
@@ -1,167 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.mapping.*;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.AbstractMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24
25public class EntryRenamer {
26
27 public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) {
28 List<T> entries = Lists.newArrayList();
29 for (T val : set) {
30 entries.add(renameClassesInThing(renames, val));
31 }
32 set.clear();
33 set.addAll(entries);
34 }
35
36 public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) {
37 // for each key/value pair...
38 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
39 for (Map.Entry<Key, Val> entry : map.entrySet()) {
40 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
41 }
42 map.clear();
43 for (Map.Entry<Key, Val> entry : entriesToAdd) {
44 map.put(entry.getKey(), entry.getValue());
45 }
46 }
47
48 public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) {
49 // for each key/value pair...
50 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
51 for (Map.Entry<Key, Val> entry : map.entries()) {
52 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue())));
53 }
54 map.clear();
55 for (Map.Entry<Key, Val> entry : entriesToAdd) {
56 map.put(entry.getKey(), entry.getValue());
57 }
58 }
59
60 public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) {
61 // for each key/value pair...
62 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
63 for (Map.Entry<Key, Val> entry : map.entries()) {
64 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
65 }
66 map.clear();
67 for (Map.Entry<Key, Val> entry : entriesToAdd) {
68 map.put(entry.getKey(), entry.getValue());
69 }
70 }
71
72 public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) {
73 // for each key/value pair...
74 Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet();
75 for (Map.Entry<Key, Val> entry : map.entrySet()) {
76 entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue())));
77 }
78 map.clear();
79 for (Map.Entry<Key, Val> entry : entriesToAdd) {
80 map.put(entry.getKey(), entry.getValue());
81 }
82 }
83
84 @SuppressWarnings("unchecked")
85 public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) {
86 if (thing instanceof MethodEntry) {
87 MethodEntry methodEntry = (MethodEntry) thing;
88 MethodEntry newMethodEntry = renames.get(methodEntry);
89 if (newMethodEntry != null) {
90 return (T) new MethodEntry(
91 methodEntry.getOwnerClassEntry(),
92 newMethodEntry.getName(),
93 methodEntry.getDesc()
94 );
95 }
96 return thing;
97 } else if (thing instanceof LocalVariableEntry) {
98 LocalVariableEntry variableEntry = (LocalVariableEntry) thing;
99 return (T) new LocalVariableEntry(
100 renameMethodsInThing(renames, variableEntry.getOwnerEntry()),
101 variableEntry.getIndex(),
102 variableEntry.getName(),
103 variableEntry.isParameter()
104 );
105 } else if (thing instanceof EntryReference) {
106 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
107 reference.entry = renameMethodsInThing(renames, reference.entry);
108 reference.context = renameMethodsInThing(renames, reference.context);
109 return thing;
110 }
111 return thing;
112 }
113
114 @SuppressWarnings("unchecked")
115 public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) {
116 if (thing instanceof String) {
117 String stringEntry = (String) thing;
118 if (renames.containsKey(stringEntry)) {
119 return (T) renames.get(stringEntry);
120 }
121 } else if (thing instanceof ClassEntry) {
122 ClassEntry classEntry = (ClassEntry) thing;
123 return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName()));
124 } else if (thing instanceof FieldDefEntry) {
125 FieldDefEntry fieldEntry = (FieldDefEntry) thing;
126 return (T) new FieldDefEntry(
127 renameClassesInThing(renames, fieldEntry.getOwnerClassEntry()),
128 fieldEntry.getName(),
129 renameClassesInThing(renames, fieldEntry.getDesc()),
130 renameClassesInThing(renames, fieldEntry.getSignature()),
131 fieldEntry.getAccess()
132 );
133 } else if (thing instanceof MethodDefEntry) {
134 MethodDefEntry methodEntry = (MethodDefEntry) thing;
135 return (T) new MethodDefEntry(
136 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
137 methodEntry.getName(),
138 renameClassesInThing(renames, methodEntry.getDesc()),
139 renameClassesInThing(renames, methodEntry.getSignature()),
140 methodEntry.getAccess()
141 );
142 } else if (thing instanceof MethodEntry) {
143 MethodEntry methodEntry = (MethodEntry) thing;
144 return (T) new MethodEntry(
145 renameClassesInThing(renames, methodEntry.getOwnerClassEntry()),
146 methodEntry.getName(),
147 renameClassesInThing(renames, methodEntry.getDesc())
148 );
149 } else if (thing instanceof LocalVariableEntry) {
150 LocalVariableEntry argumentEntry = (LocalVariableEntry) thing;
151 return (T) new LocalVariableEntry(renameClassesInThing(renames, argumentEntry.getOwnerEntry()), argumentEntry.getIndex(), argumentEntry.getName(), argumentEntry.isParameter());
152 } else if (thing instanceof EntryReference) {
153 EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing;
154 reference.entry = renameClassesInThing(renames, reference.entry);
155 reference.context = renameClassesInThing(renames, reference.context);
156 return thing;
157 } else if (thing instanceof MethodDescriptor) {
158 return (T) ((MethodDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
159 } else if (thing instanceof TypeDescriptor) {
160 return (T) ((TypeDescriptor) thing).remap(className -> renameClassesInThing(renames, className));
161 } else if (thing instanceof Signature) {
162 return (T) ((Signature) thing).remap(className -> renameClassesInThing(renames, className));
163 }
164
165 return thing;
166 }
167}
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
index 2318a2b7..4beab7f8 100644
--- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java
@@ -11,32 +11,31 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.bytecode.AccessFlags; 14import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.analysis.index.ReferenceIndex;
16import cuchaz.enigma.mapping.entry.FieldEntry; 16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.mapping.entry.MethodDefEntry; 17import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.mapping.entry.MethodEntry; 18import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
19import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 20
20import javax.swing.tree.DefaultMutableTreeNode; 21import javax.swing.tree.DefaultMutableTreeNode;
21 22
22public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { 23public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> {
23 24
24 private Translator deobfuscatingTranslator; 25 private final Translator translator;
25 private FieldEntry entry; 26 private FieldEntry entry;
26 private EntryReference<FieldEntry, MethodDefEntry> reference; 27 private EntryReference<FieldEntry, MethodDefEntry> reference;
27 private AccessFlags access;
28 28
29 public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { 29 public FieldReferenceTreeNode(Translator translator, FieldEntry entry) {
30 this.deobfuscatingTranslator = deobfuscatingTranslator; 30 this.translator = translator;
31 this.entry = entry; 31 this.entry = entry;
32 this.reference = null; 32 this.reference = null;
33 } 33 }
34 34
35 private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, MethodDefEntry> reference, AccessFlags access) { 35 private FieldReferenceTreeNode(Translator translator, EntryReference<FieldEntry, MethodDefEntry> reference) {
36 this.deobfuscatingTranslator = deobfuscatingTranslator; 36 this.translator = translator;
37 this.entry = reference.entry; 37 this.entry = reference.entry;
38 this.reference = reference; 38 this.reference = reference;
39 this.access = access;
40 } 39 }
41 40
42 @Override 41 @Override
@@ -52,27 +51,29 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re
52 @Override 51 @Override
53 public String toString() { 52 public String toString() {
54 if (this.reference != null) { 53 if (this.reference != null) {
55 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), this.access); 54 return String.format("%s", translator.translate(this.reference.context));
56 } 55 }
57 return deobfuscatingTranslator.getTranslatedField(entry).getName(); 56 return translator.translate(entry).toString();
58 } 57 }
59 58
60 public void load(JarIndex index, boolean recurse) { 59 public void load(JarIndex index, boolean recurse) {
60 ReferenceIndex referenceIndex = index.getReferenceIndex();
61
61 // get all the child nodes 62 // get all the child nodes
62 if (this.reference == null) { 63 if (this.reference == null) {
63 for (EntryReference<FieldEntry, MethodDefEntry> reference : index.getFieldReferences(this.entry)) { 64 for (EntryReference<FieldEntry, MethodDefEntry> reference : referenceIndex.getReferencesToField(this.entry)) {
64 add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 65 add(new FieldReferenceTreeNode(translator, reference));
65 } 66 }
66 } else { 67 } else {
67 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.reference.context)) { 68 for (EntryReference<MethodEntry, MethodDefEntry> reference : referenceIndex.getReferencesToMethod(this.reference.context)) {
68 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.reference.context))); 69 add(new MethodReferenceTreeNode(translator, reference));
69 } 70 }
70 } 71 }
71 72
72 if (recurse && children != null) { 73 if (recurse && children != null) {
73 for (Object node : children) { 74 for (Object node : children) {
74 if (node instanceof MethodReferenceTreeNode) { 75 if (node instanceof MethodReferenceTreeNode) {
75 ((MethodReferenceTreeNode) node).load(index, true); 76 ((MethodReferenceTreeNode) node).load(index, true, false);
76 } else if (node instanceof FieldReferenceTreeNode) { 77 } else if (node instanceof FieldReferenceTreeNode) {
77 ((FieldReferenceTreeNode) node).load(index, true); 78 ((FieldReferenceTreeNode) node).load(index, true);
78 } 79 }
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
deleted file mode 100644
index b6ab2d5b..00000000
--- a/src/main/java/cuchaz/enigma/analysis/IndexInnerClassVisitor.java
+++ /dev/null
@@ -1,29 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.mapping.entry.ClassEntry;
4import org.objectweb.asm.ClassVisitor;
5
6public class IndexInnerClassVisitor extends ClassVisitor {
7 private final JarIndex index;
8
9 public IndexInnerClassVisitor(JarIndex index, int api) {
10 super(api);
11 this.index = index;
12 }
13
14 public IndexInnerClassVisitor(JarIndex index, int api, ClassVisitor cv) {
15 super(api, cv);
16 this.index = index;
17 }
18
19 @Override
20 public void visitInnerClass(String name, String outerName, String innerName, int access) {
21 ClassEntry entry = new ClassEntry(name);
22 // Ignore anonymous classes
23 if (innerName != null && outerName != null) {
24 ClassEntry outerEntry = new ClassEntry(outerName);
25 index.indexInnerClass(entry, outerEntry);
26 }
27 super.visitInnerClass(name, outerName, innerName, access);
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
deleted file mode 100644
index f37f1e90..00000000
--- a/src/main/java/cuchaz/enigma/analysis/IndexReferenceVisitor.java
+++ /dev/null
@@ -1,77 +0,0 @@
1package cuchaz.enigma.analysis;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.mapping.Signature;
6import cuchaz.enigma.mapping.entry.ClassEntry;
7import cuchaz.enigma.mapping.entry.MethodDefEntry;
8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.Handle;
10import org.objectweb.asm.MethodVisitor;
11import org.objectweb.asm.Opcodes;
12
13public class IndexReferenceVisitor extends ClassVisitor {
14 private final JarIndex index;
15 private ClassEntry classEntry;
16
17 public IndexReferenceVisitor(JarIndex index, int api) {
18 super(api);
19 this.index = index;
20 }
21
22 @Override
23 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 this.classEntry = new ClassEntry(name);
25 }
26
27 @Override
28 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
29 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
30 return new Method(this.index, entry, this.api);
31 }
32
33 private class Method extends MethodVisitor {
34 private final JarIndex index;
35 private final MethodDefEntry callerEntry;
36
37 public Method(JarIndex index, MethodDefEntry callerEntry, int api) {
38 super(api);
39 this.index = index;
40 this.callerEntry = callerEntry;
41 }
42
43 @Override
44 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
45 this.index.indexFieldAccess(callerEntry, owner, name, desc);
46 }
47
48 @Override
49 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
50 this.index.indexMethodCall(callerEntry, owner, name, desc);
51 }
52
53 @Override
54 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
55 for (Object bsmArg : bsmArgs){
56 if (bsmArg instanceof Handle){
57 Handle handle = (Handle)bsmArg;
58 switch (handle.getTag()){
59 case Opcodes.H_GETFIELD:
60 case Opcodes.H_GETSTATIC:
61 case Opcodes.H_PUTFIELD:
62 case Opcodes.H_PUTSTATIC:
63 this.index.indexFieldAccess(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
64 break;
65 case Opcodes.H_INVOKEINTERFACE:
66 case Opcodes.H_INVOKESPECIAL:
67 case Opcodes.H_INVOKESTATIC:
68 case Opcodes.H_INVOKEVIRTUAL:
69 case Opcodes.H_NEWINVOKESPECIAL:
70 this.index.indexMethodCall(callerEntry, handle.getOwner(), handle.getName(), handle.getDesc());
71 break;
72 }
73 }
74 }
75 }
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
new file mode 100644
index 00000000..4ca7cd11
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java
@@ -0,0 +1,87 @@
1package cuchaz.enigma.analysis;
2
3import com.google.common.collect.Lists;
4import cuchaz.enigma.analysis.index.EntryIndex;
5import cuchaz.enigma.analysis.index.JarIndex;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10
11import java.util.List;
12
13public class IndexTreeBuilder {
14 private final JarIndex index;
15
16 public IndexTreeBuilder(JarIndex index) {
17 this.index = index;
18 }
19
20 public ClassInheritanceTreeNode buildClassInheritance(Translator translator, ClassEntry obfClassEntry) {
21 // get the root node
22 List<String> ancestry = Lists.newArrayList();
23 ancestry.add(obfClassEntry.getFullName());
24 for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) {
25 ancestry.add(classEntry.getFullName());
26 }
27
28 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(translator, ancestry.get(ancestry.size() - 1));
29
30 // expand all children recursively
31 rootNode.load(index.getInheritanceIndex(), true);
32
33 return rootNode;
34 }
35
36 public ClassImplementationsTreeNode buildClassImplementations(Translator translator, ClassEntry obfClassEntry) {
37 if (index.getInheritanceIndex().isParent(obfClassEntry)) {
38 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(translator, obfClassEntry);
39 node.load(index);
40 return node;
41 }
42 return null;
43 }
44
45 public MethodInheritanceTreeNode buildMethodInheritance(Translator translator, MethodEntry obfMethodEntry) {
46 MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT);
47
48 // make a root node at the base
49 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
50 translator, resolvedEntry,
51 index.getEntryIndex().hasMethod(resolvedEntry)
52 );
53
54 // expand the full tree
55 rootNode.load(index, true);
56
57 return rootNode;
58 }
59
60 public List<MethodImplementationsTreeNode> buildMethodImplementations(Translator translator, MethodEntry obfMethodEntry) {
61 EntryIndex entryIndex = index.getEntryIndex();
62
63 List<MethodEntry> ancestorMethodEntries = Lists.newArrayList();
64
65 if (entryIndex.hasMethod(obfMethodEntry)) {
66 ancestorMethodEntries.add(obfMethodEntry);
67 }
68
69 for (ClassEntry ancestorEntry : index.getInheritanceIndex().getAncestors(obfMethodEntry.getParent())) {
70 MethodEntry ancestorMethod = obfMethodEntry.withParent(ancestorEntry);
71 if (entryIndex.hasMethod(ancestorMethod)) {
72 ancestorMethodEntries.add(ancestorMethod);
73 }
74 }
75
76 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
77 if (!ancestorMethodEntries.isEmpty()) {
78 for (MethodEntry interfaceMethodEntry : ancestorMethodEntries) {
79 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, interfaceMethodEntry);
80 node.load(index);
81 nodes.add(node);
82 }
83 }
84
85 return nodes;
86 }
87}
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
deleted file mode 100644
index 361c8e73..00000000
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ /dev/null
@@ -1,583 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.collect.*;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.*;
17import cuchaz.enigma.mapping.entry.*;
18import org.objectweb.asm.ClassReader;
19import org.objectweb.asm.ClassVisitor;
20import org.objectweb.asm.Opcodes;
21
22import java.util.*;
23
24public class JarIndex {
25
26 private final ReferencedEntryPool entryPool;
27
28 private Set<ClassEntry> obfClassEntries;
29 private TranslationIndex translationIndex;
30 private Map<Entry, AccessFlags> access;
31 private Multimap<ClassEntry, FieldDefEntry> fields;
32 private Multimap<ClassEntry, MethodDefEntry> methods;
33 private Multimap<String, MethodDefEntry> methodImplementations;
34 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> methodsReferencing;
35 private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> methodsReferencingClasses;
36 private Multimap<MethodEntry, MethodEntry> methodReferences;
37 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> fieldReferences;
38 private Multimap<ClassEntry, ClassEntry> innerClassesByOuter;
39 private Map<ClassEntry, ClassEntry> outerClassesByInner;
40 private Map<MethodEntry, MethodEntry> bridgedMethods;
41 private Set<MethodEntry> syntheticMethods;
42
43 public JarIndex(ReferencedEntryPool entryPool) {
44 this.entryPool = entryPool;
45 this.obfClassEntries = Sets.newHashSet();
46 this.translationIndex = new TranslationIndex(entryPool);
47 this.access = Maps.newHashMap();
48 this.fields = HashMultimap.create();
49 this.methods = HashMultimap.create();
50 this.methodImplementations = HashMultimap.create();
51 this.methodsReferencingClasses = HashMultimap.create();
52 this.methodsReferencing = HashMultimap.create();
53 this.methodReferences = HashMultimap.create();
54 this.fieldReferences = HashMultimap.create();
55 this.innerClassesByOuter = HashMultimap.create();
56 this.outerClassesByInner = Maps.newHashMap();
57 this.bridgedMethods = Maps.newHashMap();
58 this.syntheticMethods = Sets.newHashSet();
59 }
60
61 public void indexJar(ParsedJar jar, boolean buildInnerClasses) {
62
63 // step 1: read the class names
64 obfClassEntries.addAll(jar.getClassEntries());
65
66 // step 2: index classes, fields, methods, interfaces
67 if (buildInnerClasses) {
68 // + step 5: index inner classes
69 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5, new IndexInnerClassVisitor(this, Opcodes.ASM5)), ClassReader.SKIP_CODE);
70 } else {
71 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
72 }
73
74 // step 3: index field, method, constructor references
75 jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES);
76
77 // step 4: index access and bridged methods
78 for (MethodDefEntry methodEntry : methods.values()) {
79 // look for access and bridged methods
80 MethodEntry accessedMethod = findAccessMethod(methodEntry);
81 if (accessedMethod != null) {
82 if (isBridgedMethod(accessedMethod, methodEntry)) {
83 this.bridgedMethods.put(methodEntry, accessedMethod);
84 }
85 }
86 }
87
88 if (buildInnerClasses) {
89 // step 6: update other indices with inner class info
90 Map<String, String> renames = Maps.newHashMap();
91 for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) {
92 String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName();
93 if (!innerClassEntry.getName().equals(newName)) {
94 // DEBUG
95 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName);
96 renames.put(innerClassEntry.getName(), newName);
97 }
98 }
99 EntryRenamer.renameClassesInSet(renames, this.obfClassEntries);
100 this.translationIndex.renameClasses(renames);
101 EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations);
102 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencingClasses);
103 EntryRenamer.renameClassesInMultimap(renames, this.methodsReferencing);
104 EntryRenamer.renameClassesInMultimap(renames, this.methodReferences);
105 EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences);
106 EntryRenamer.renameClassesInMap(renames, this.access);
107 }
108 }
109
110 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
111 for (String interfaceName : interfaces) {
112 if (name.equals(interfaceName)) {
113 throw new IllegalArgumentException("Class cannot be its own interface! " + name);
114 }
115 }
116 ClassDefEntry entry = this.translationIndex.indexClass(access, name, signature, superName, interfaces);
117 this.access.put(entry, entry.getAccess());
118 return entry;
119 }
120
121 protected void indexField(ClassDefEntry owner, int access, String name, String desc, String signature) {
122 FieldDefEntry fieldEntry = new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
123 this.translationIndex.indexField(fieldEntry);
124 this.access.put(fieldEntry, fieldEntry.getAccess());
125 this.fields.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
126 }
127
128 protected void indexMethod(ClassDefEntry owner, int access, String name, String desc, String signature) {
129 MethodDefEntry methodEntry = new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
130 this.translationIndex.indexMethod(methodEntry);
131 this.access.put(methodEntry, methodEntry.getAccess());
132 this.methods.put(methodEntry.getOwnerClassEntry(), methodEntry);
133
134 if (new AccessFlags(access).isSynthetic()) {
135 syntheticMethods.add(methodEntry);
136 }
137
138 // we don't care about constructors here
139 if (!methodEntry.isConstructor()) {
140 // index implementation
141 this.methodImplementations.put(methodEntry.getClassName(), methodEntry);
142 }
143 }
144
145 protected void indexMethodCall(MethodDefEntry callerEntry, String owner, String name, String desc) {
146 ClassEntry referencedClass = entryPool.getClass(owner);
147 MethodEntry referencedMethod = new MethodEntry(referencedClass, name, new MethodDescriptor(desc));
148 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedMethod);
149 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedMethod.getOwnerClassEntry())) {
150 referencedMethod = referencedMethod.updateOwnership(resolvedClassEntry);
151 }
152 methodsReferencing.put(referencedMethod, new EntryReference<>(referencedMethod, referencedMethod.getName(), callerEntry));
153 if (referencedMethod.isConstructor()) {
154 methodsReferencingClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedMethod.getName(), callerEntry));
155 }
156 methodReferences.put(callerEntry, referencedMethod);
157 }
158
159 protected void indexFieldAccess(MethodDefEntry callerEntry, String owner, String name, String desc) {
160 FieldEntry referencedField = new FieldEntry(entryPool.getClass(owner), name, new TypeDescriptor(desc));
161 ClassEntry resolvedClassEntry = translationIndex.resolveEntryOwner(referencedField);
162 if (resolvedClassEntry != null && !resolvedClassEntry.equals(referencedField.getOwnerClassEntry())) {
163 referencedField = referencedField.updateOwnership(resolvedClassEntry);
164 }
165 fieldReferences.put(referencedField, new EntryReference<>(referencedField, referencedField.getName(), callerEntry));
166 }
167
168 public void indexInnerClass(ClassEntry innerEntry, ClassEntry outerEntry) {
169 this.innerClassesByOuter.put(outerEntry, innerEntry);
170 this.outerClassesByInner.putIfAbsent(innerEntry, outerEntry);
171 }
172
173 private MethodEntry findAccessMethod(MethodDefEntry method) {
174
175 // we want to find all compiler-added methods that directly call another with no processing
176
177 // skip non-synthetic methods
178 if (!method.getAccess().isSynthetic()) {
179 return null;
180 }
181
182 // get all the methods that we call
183 final Collection<MethodEntry> referencedMethods = methodReferences.get(method);
184
185 // is there just one?
186 if (referencedMethods.size() != 1) {
187 return null;
188 }
189
190 return referencedMethods.stream().findFirst().orElse(null);
191 }
192
193 private boolean isBridgedMethod(MethodEntry called, MethodEntry access) {
194 // Bridged methods will always have the same name as the method they are calling
195 // They will also have the same amount of parameters (though equal descriptors cannot be guaranteed)
196 if (!called.getName().equals(access.getName()) || called.getDesc().getArgumentDescs().size() != access.getDesc().getArgumentDescs().size()) {
197 return false;
198 }
199
200 TypeDescriptor accessReturn = access.getDesc().getReturnDesc();
201 TypeDescriptor calledReturn = called.getDesc().getReturnDesc();
202 if (calledReturn.isVoid() || calledReturn.isPrimitive() || accessReturn.isVoid() || accessReturn.isPrimitive()) {
203 return false;
204 }
205
206 // Bridged methods will never have the same type as what they are calling
207 if (accessReturn.equals(calledReturn)) {
208 return false;
209 }
210
211 String accessType = accessReturn.toString();
212
213 // If we're casting down from generic type to type-erased Object we're a bridge method
214 if (accessType.equals("Ljava/lang/Object;")) {
215 return true;
216 }
217
218 // Now we need to detect cases where we are being casted down to a higher type bound
219 List<ClassEntry> calledAncestry = translationIndex.getAncestry(calledReturn.getTypeEntry());
220 return calledAncestry.contains(accessReturn.getTypeEntry());
221 }
222
223 public Set<ClassEntry> getObfClassEntries() {
224 return this.obfClassEntries;
225 }
226
227 public Collection<FieldDefEntry> getObfFieldEntries() {
228 return this.fields.values();
229 }
230
231 public Collection<FieldDefEntry> getObfFieldEntries(ClassEntry classEntry) {
232 return this.fields.get(classEntry);
233 }
234
235 public Collection<MethodDefEntry> getObfBehaviorEntries() {
236 return this.methods.values();
237 }
238
239 public Collection<MethodDefEntry> getObfBehaviorEntries(ClassEntry classEntry) {
240 return this.methods.get(classEntry);
241 }
242
243 public TranslationIndex getTranslationIndex() {
244 return this.translationIndex;
245 }
246
247 @Deprecated
248 public Access getAccess(Entry entry) {
249 AccessFlags flags = getAccessFlags(entry);
250 return flags != null ? Access.get(flags) : null;
251 }
252
253 public AccessFlags getAccessFlags(Entry entry) {
254 return this.access.get(entry);
255 }
256
257 public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
258
259 // get the root node
260 List<String> ancestry = Lists.newArrayList();
261 ancestry.add(obfClassEntry.getName());
262 for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) {
263 if (containsObfClass(classEntry)) {
264 ancestry.add(classEntry.getName());
265 }
266 }
267 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
268 deobfuscatingTranslator,
269 ancestry.get(ancestry.size() - 1)
270 );
271
272 // expand all children recursively
273 rootNode.load(this.translationIndex, true);
274
275 return rootNode;
276 }
277
278 public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) {
279
280 // is this even an interface?
281 if (isInterface(obfClassEntry.getClassName())) {
282 ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry);
283 node.load(this);
284 return node;
285 }
286 return null;
287 }
288
289 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
290 // travel to the ancestor implementation
291 LinkedList<ClassEntry> entries = new LinkedList<>();
292 entries.add(obfMethodEntry.getOwnerClassEntry());
293
294 // TODO: This could be optimized to not go through interfaces repeatedly...
295
296 ClassEntry baseImplementationClassEntry = obfMethodEntry.getOwnerClassEntry();
297
298 for (ClassEntry itf : getInterfaces(obfMethodEntry.getOwnerClassEntry().getClassName())) {
299 MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
300 if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) {
301 baseImplementationClassEntry = itf;
302 }
303 }
304
305 for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(entries.remove())) {
306 MethodEntry ancestorMethodEntry = entryPool.getMethod(ancestorClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
307 if (ancestorMethodEntry != null) {
308 if (containsObfMethod(ancestorMethodEntry)) {
309 baseImplementationClassEntry = ancestorClassEntry;
310 }
311
312 for (ClassEntry itf : getInterfaces(ancestorClassEntry.getClassName())) {
313 MethodEntry itfMethodEntry = entryPool.getMethod(itf, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
314 if (itfMethodEntry != null && containsObfMethod(itfMethodEntry)) {
315 baseImplementationClassEntry = itf;
316 }
317 }
318 }
319 }
320
321 // make a root node at the base
322 MethodEntry methodEntry = entryPool.getMethod(baseImplementationClassEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
323 MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(
324 deobfuscatingTranslator,
325 methodEntry,
326 containsObfMethod(methodEntry)
327 );
328
329 // expand the full tree
330 rootNode.load(this, true);
331
332 return rootNode;
333 }
334
335 public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
336
337 List<MethodEntry> interfaceMethodEntries = Lists.newArrayList();
338
339 // is this method on an interface?
340 if (isInterface(obfMethodEntry.getClassName())) {
341 interfaceMethodEntries.add(obfMethodEntry);
342 } else {
343 // get the interface class
344 for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) {
345
346 // is this method defined in this interface?
347 MethodEntry methodInterface = entryPool.getMethod(interfaceEntry, obfMethodEntry.getName(), obfMethodEntry.getDesc().toString());
348 if (methodInterface != null && containsObfMethod(methodInterface)) {
349 interfaceMethodEntries.add(methodInterface);
350 }
351 }
352 }
353
354 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
355 if (!interfaceMethodEntries.isEmpty()) {
356 for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) {
357 MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry);
358 node.load(this);
359 nodes.add(node);
360 }
361 }
362 return nodes;
363 }
364
365 public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) {
366 AccessFlags flags = getAccessFlags(obfMethodEntry);
367 if (flags.isPrivate() || flags.isStatic()) {
368 return Collections.singleton(obfMethodEntry);
369 }
370
371 Set<MethodEntry> methodEntries = Sets.newHashSet();
372 getRelatedMethodImplementations(methodEntries, getMethodInheritance(new DirectionalTranslator(entryPool), obfMethodEntry));
373 return methodEntries;
374 }
375
376 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
377 MethodEntry methodEntry = node.getMethodEntry();
378 if (methodEntries.contains(methodEntry)) {
379 return;
380 }
381
382 if (containsObfMethod(methodEntry)) {
383 AccessFlags flags = getAccessFlags(methodEntry);
384 if (!flags.isPrivate() && !flags.isStatic()) {
385 // collect the entry
386 methodEntries.add(methodEntry);
387 }
388 }
389
390 // look at bridge methods!
391 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
392 while (bridgedMethod != null) {
393 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
394 bridgedMethod = getBridgedMethod(bridgedMethod);
395 }
396
397 // look at interface methods too
398 for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new DirectionalTranslator(entryPool), methodEntry)) {
399 getRelatedMethodImplementations(methodEntries, implementationsNode);
400 }
401
402 // recurse
403 for (int i = 0; i < node.getChildCount(); i++) {
404 getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
405 }
406 }
407
408 private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
409 MethodEntry methodEntry = node.getMethodEntry();
410 if (containsObfMethod(methodEntry)) {
411 AccessFlags flags = getAccessFlags(methodEntry);
412 if (!flags.isPrivate() && !flags.isStatic()) {
413 // collect the entry
414 methodEntries.add(methodEntry);
415 }
416 }
417
418 // look at bridge methods!
419 MethodEntry bridgedMethod = getBridgedMethod(methodEntry);
420 while (bridgedMethod != null) {
421 methodEntries.addAll(getRelatedMethodImplementations(bridgedMethod));
422 bridgedMethod = getBridgedMethod(bridgedMethod);
423 }
424
425 // recurse
426 for (int i = 0; i < node.getChildCount(); i++) {
427 getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
428 }
429 }
430
431 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getFieldReferences(FieldEntry fieldEntry) {
432 return this.fieldReferences.get(fieldEntry);
433 }
434
435 public Collection<FieldEntry> getReferencedFields(MethodDefEntry methodEntry) {
436 // linear search is fast enough for now
437 Set<FieldEntry> fieldEntries = Sets.newHashSet();
438 for (EntryReference<FieldEntry, MethodDefEntry> reference : this.fieldReferences.values()) {
439 if (reference.context == methodEntry) {
440 fieldEntries.add(reference.entry);
441 }
442 }
443 return fieldEntries;
444 }
445
446 public Collection<EntryReference<ClassEntry, MethodDefEntry>> getMethodsReferencing(ClassEntry classEntry) {
447 return this.methodsReferencingClasses.get(classEntry);
448 }
449
450 @Deprecated
451 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry) {
452 return getMethodsReferencing(methodEntry, false);
453 }
454
455 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getMethodsReferencing(MethodEntry methodEntry, boolean recurse) {
456 if (!recurse) {
457 return this.methodsReferencing.get(methodEntry);
458 }
459
460 List<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>();
461 Set<MethodEntry> methodEntries = getRelatedMethodImplementations(methodEntry);
462 for (MethodEntry entry : methodEntries) {
463 references.addAll(getMethodsReferencing(entry, false));
464 }
465 return references;
466 }
467
468 public Collection<MethodEntry> getReferencedMethods(MethodDefEntry methodEntry) {
469 return this.methodReferences.get(methodEntry);
470 }
471
472 public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) {
473 return this.innerClassesByOuter.get(obfOuterClassEntry);
474 }
475
476 public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) {
477 return this.outerClassesByInner.get(obfInnerClassEntry);
478 }
479
480 public boolean isSyntheticMethod(MethodEntry methodEntry) {
481 return this.syntheticMethods.contains(methodEntry);
482 }
483
484 public Set<ClassEntry> getInterfaces(String className) {
485 ClassEntry classEntry = entryPool.getClass(className);
486 Set<ClassEntry> interfaces = new HashSet<>(this.translationIndex.getInterfaces(classEntry));
487 for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) {
488 interfaces.addAll(this.translationIndex.getInterfaces(ancestor));
489 }
490 return interfaces;
491 }
492
493 public Set<String> getImplementingClasses(String targetInterfaceName) {
494
495 // linear search is fast enough for now
496 Set<String> classNames = Sets.newHashSet();
497 for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) {
498 ClassEntry classEntry = entry.getKey();
499 ClassEntry interfaceEntry = entry.getValue();
500 if (interfaceEntry.getName().equals(targetInterfaceName)) {
501 String className = classEntry.getClassName();
502 classNames.add(className);
503 if (isInterface(className)) {
504 classNames.addAll(getImplementingClasses(className));
505 }
506
507 this.translationIndex.getSubclassNamesRecursively(classNames, classEntry);
508 }
509 }
510 return classNames;
511 }
512
513 public boolean isInterface(String className) {
514 return this.translationIndex.isInterface(entryPool.getClass(className));
515 }
516
517 public boolean containsObfClass(ClassEntry obfClassEntry) {
518 return this.obfClassEntries.contains(obfClassEntry);
519 }
520
521 public boolean containsObfField(FieldEntry obfFieldEntry) {
522 return this.access.containsKey(obfFieldEntry);
523 }
524
525 public boolean containsObfMethod(MethodEntry obfMethodEntry) {
526 return this.access.containsKey(obfMethodEntry);
527 }
528
529 public boolean containsEntryWithSameName(Entry entry) {
530 for (Entry target : this.access.keySet())
531 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass()))
532 return true;
533 return false;
534 }
535
536 public boolean containsObfVariable(LocalVariableEntry obfVariableEntry) {
537 // check the behavior
538 if (!containsObfMethod(obfVariableEntry.getOwnerEntry())) {
539 return false;
540 }
541
542 return true;
543 }
544
545 public boolean containsObfEntry(Entry obfEntry) {
546 if (obfEntry instanceof ClassEntry) {
547 return containsObfClass((ClassEntry) obfEntry);
548 } else if (obfEntry instanceof FieldEntry) {
549 return containsObfField((FieldEntry) obfEntry);
550 } else if (obfEntry instanceof MethodEntry) {
551 return containsObfMethod((MethodEntry) obfEntry);
552 } else if (obfEntry instanceof LocalVariableEntry) {
553 return containsObfVariable((LocalVariableEntry) obfEntry);
554 } else {
555 throw new Error("Entry desc not supported: " + obfEntry.getClass().getName());
556 }
557 }
558
559 public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) {
560 return this.bridgedMethods.get(bridgeMethodEntry);
561 }
562
563 public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) {
564
565 // build class chain in inner-to-outer order
566 List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry);
567 ClassEntry checkClassEntry = obfClassEntry;
568 while (true) {
569 ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry);
570 if (obfOuterClassEntry != null) {
571 obfClassChain.add(obfOuterClassEntry);
572 checkClassEntry = obfOuterClassEntry;
573 } else {
574 break;
575 }
576 }
577
578 // switch to outer-to-inner order
579 Collections.reverse(obfClassChain);
580
581 return obfClassChain;
582 }
583}
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
index 4b47c5f0..e4b03043 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java
@@ -12,24 +12,28 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 21
19import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
23import java.util.Collection;
20import java.util.List; 24import java.util.List;
21 25
22public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { 26public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
23 27
24 private Translator deobfuscatingTranslator; 28 private final Translator translator;
25 private MethodEntry entry; 29 private MethodEntry entry;
26 30
27 public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 31 public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) {
32 this.translator = translator;
28 if (entry == null) { 33 if (entry == null) {
29 throw new IllegalArgumentException("Entry cannot be null!"); 34 throw new IllegalArgumentException("Entry cannot be null!");
30 } 35 }
31 36
32 this.deobfuscatingTranslator = deobfuscatingTranslator;
33 this.entry = entry; 37 this.entry = entry;
34 } 38 }
35 39
@@ -53,35 +57,25 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode {
53 return this.entry; 57 return this.entry;
54 } 58 }
55 59
56 public String getDeobfClassName() {
57 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getClassName();
58 }
59
60 public String getDeobfMethodName() {
61 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
62 }
63
64 @Override 60 @Override
65 public String toString() { 61 public String toString() {
66 String className = getDeobfClassName(); 62 MethodEntry translatedEntry = translator.translate(entry);
67 if (className == null) { 63 String className = translatedEntry.getParent().getFullName();
68 className = this.entry.getClassName(); 64 String methodName = translatedEntry.getName();
69 }
70
71 String methodName = getDeobfMethodName();
72 if (methodName == null) {
73 methodName = this.entry.getName();
74 }
75 return className + "." + methodName + "()"; 65 return className + "." + methodName + "()";
76 } 66 }
77 67
78 public void load(JarIndex index) { 68 public void load(JarIndex index) {
79 // get all method implementations 69 // get all method implementations
80 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); 70 List<MethodImplementationsTreeNode> nodes = Lists.newArrayList();
81 for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { 71 EntryIndex entryIndex = index.getEntryIndex();
82 MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getDesc()); 72 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
83 if (index.containsObfMethod(methodEntry)) { 73
84 nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); 74 Collection<ClassEntry> inheritors = inheritanceIndex.getChildren(entry.getParent());
75 for (ClassEntry inheritor : inheritors) {
76 MethodEntry methodEntry = entry.withParent(inheritor);
77 if (entryIndex.hasMethod(methodEntry)) {
78 nodes.add(new MethodImplementationsTreeNode(translator, methodEntry));
85 } 79 }
86 } 80 }
87 81
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index dc341979..f0fd1d28 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -12,21 +12,24 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.mapping.entry.MethodEntry; 16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.mapping.Translator; 17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.Translator;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
18 21
19import javax.swing.tree.DefaultMutableTreeNode; 22import javax.swing.tree.DefaultMutableTreeNode;
20import java.util.List; 23import java.util.List;
21 24
22public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { 25public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
23 26
24 private Translator deobfuscatingTranslator; 27 private final Translator translator;
25 private MethodEntry entry; 28 private MethodEntry entry;
26 private boolean isImplemented; 29 private boolean isImplemented;
27 30
28 public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { 31 public MethodInheritanceTreeNode(Translator translator, MethodEntry entry, boolean isImplemented) {
29 this.deobfuscatingTranslator = deobfuscatingTranslator; 32 this.translator = translator;
30 this.entry = entry; 33 this.entry = entry;
31 this.isImplemented = isImplemented; 34 this.isImplemented = isImplemented;
32 } 35 }
@@ -51,32 +54,19 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
51 return this.entry; 54 return this.entry;
52 } 55 }
53 56
54 public String getDeobfClassName() {
55 return this.deobfuscatingTranslator.getTranslatedClass(this.entry.getOwnerClassEntry()).getName();
56 }
57
58 public String getDeobfMethodName() {
59 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName();
60 }
61
62 public boolean isImplemented() { 57 public boolean isImplemented() {
63 return this.isImplemented; 58 return this.isImplemented;
64 } 59 }
65 60
66 @Override 61 @Override
67 public String toString() { 62 public String toString() {
68 String className = getDeobfClassName(); 63 MethodEntry translatedEntry = translator.translate(entry);
69 if (className == null) { 64 String className = translatedEntry.getContainingClass().getFullName();
70 className = this.entry.getClassName();
71 }
72 65
73 if (!this.isImplemented) { 66 if (!this.isImplemented) {
74 return className; 67 return className;
75 } else { 68 } else {
76 String methodName = getDeobfMethodName(); 69 String methodName = translatedEntry.getName();
77 if (methodName == null) {
78 methodName = this.entry.getName();
79 }
80 return className + "." + methodName + "()"; 70 return className + "." + methodName + "()";
81 } 71 }
82 } 72 }
@@ -84,14 +74,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
84 public void load(JarIndex index, boolean recurse) { 74 public void load(JarIndex index, boolean recurse) {
85 // get all the child nodes 75 // get all the child nodes
86 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 76 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
87 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getOwnerClassEntry())) { 77 EntryIndex entryIndex = index.getEntryIndex();
88 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); 78 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
89 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry)));
90 }
91 79
92 for (ClassEntry subclassEntry : index.getTranslationIndex().getImplementers(this.entry.getOwnerClassEntry())) { 80 for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) {
93 MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getDesc()); 81 MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc());
94 nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfMethod(methodEntry))); 82 nodes.add(new MethodInheritanceTreeNode(translator, methodEntry, entryIndex.hasMethod(methodEntry)));
95 } 83 }
96 84
97 // add them to this node 85 // add them to this node
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
index ac05acdc..8995eb5c 100644
--- a/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java
@@ -12,36 +12,36 @@
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.mapping.*; 16import cuchaz.enigma.analysis.index.ReferenceIndex;
17import cuchaz.enigma.mapping.entry.Entry; 17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.mapping.entry.MethodDefEntry; 18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.mapping.entry.MethodEntry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
21import cuchaz.enigma.translation.representation.entry.MethodEntry;
20 22
21import javax.swing.tree.DefaultMutableTreeNode; 23import javax.swing.tree.DefaultMutableTreeNode;
22import javax.swing.tree.TreeNode; 24import javax.swing.tree.TreeNode;
25import java.util.ArrayList;
26import java.util.Collection;
23import java.util.Set; 27import java.util.Set;
24 28
25public class MethodReferenceTreeNode extends DefaultMutableTreeNode 29public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
26 implements ReferenceTreeNode<MethodEntry, MethodDefEntry> {
27 30
28 private Translator deobfuscatingTranslator; 31 private final Translator translator;
29 private MethodEntry entry; 32 private MethodEntry entry;
30 private EntryReference<MethodEntry, MethodDefEntry> reference; 33 private EntryReference<MethodEntry, MethodDefEntry> reference;
31 private AccessFlags access;
32 34
33 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { 35 public MethodReferenceTreeNode(Translator translator, MethodEntry entry) {
34 this.deobfuscatingTranslator = deobfuscatingTranslator; 36 this.translator = translator;
35 this.entry = entry; 37 this.entry = entry;
36 this.reference = null; 38 this.reference = null;
37 } 39 }
38 40
39 public MethodReferenceTreeNode(Translator deobfuscatingTranslator, 41 public MethodReferenceTreeNode(Translator translator, EntryReference<MethodEntry, MethodDefEntry> reference) {
40 EntryReference<MethodEntry, MethodDefEntry> reference, AccessFlags access) { 42 this.translator = translator;
41 this.deobfuscatingTranslator = deobfuscatingTranslator;
42 this.entry = reference.entry; 43 this.entry = reference.entry;
43 this.reference = reference; 44 this.reference = reference;
44 this.access = access;
45 } 45 }
46 46
47 @Override 47 @Override
@@ -57,21 +57,17 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
57 @Override 57 @Override
58 public String toString() { 58 public String toString() {
59 if (this.reference != null) { 59 if (this.reference != null) {
60 return String.format("%s (%s)", this.deobfuscatingTranslator.getTranslatedMethodDef(this.reference.context), 60 return String.format("%s", translator.translate(this.reference.context));
61 this.access);
62 } 61 }
63 return this.deobfuscatingTranslator.getTranslatedMethod(this.entry).getName(); 62 return translator.translate(this.entry).getName();
64 }
65
66 @Deprecated
67 public void load(JarIndex index, boolean recurse) {
68 load(index, recurse, false);
69 } 63 }
70 64
71 public void load(JarIndex index, boolean recurse, boolean recurseMethod) { 65 public void load(JarIndex index, boolean recurse, boolean recurseMethod) {
72 // get all the child nodes 66 // get all the child nodes
73 for (EntryReference<MethodEntry, MethodDefEntry> reference : index.getMethodsReferencing(this.entry, recurseMethod)) { 67 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = getReferences(index, recurseMethod);
74 add(new MethodReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccessFlags(this.entry))); 68
69 for (EntryReference<MethodEntry, MethodDefEntry> reference : references) {
70 add(new MethodReferenceTreeNode(translator, reference));
75 } 71 }
76 72
77 if (recurse && this.children != null) { 73 if (recurse && this.children != null) {
@@ -80,7 +76,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
80 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child; 76 MethodReferenceTreeNode node = (MethodReferenceTreeNode) child;
81 77
82 // don't recurse into ancestor 78 // don't recurse into ancestor
83 Set<Entry> ancestors = Sets.newHashSet(); 79 Set<Entry<?>> ancestors = Sets.newHashSet();
84 TreeNode n = node; 80 TreeNode n = node;
85 while (n.getParent() != null) { 81 while (n.getParent() != null) {
86 n = n.getParent(); 82 n = n.getParent();
@@ -92,9 +88,26 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode
92 continue; 88 continue;
93 } 89 }
94 90
95 node.load(index, true); 91 node.load(index, true, false);
96 } 92 }
97 } 93 }
98 } 94 }
99 } 95 }
96
97 private Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferences(JarIndex index, boolean recurseMethod) {
98 ReferenceIndex referenceIndex = index.getReferenceIndex();
99
100 if (recurseMethod) {
101 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = new ArrayList<>();
102
103 EntryResolver entryResolver = index.getEntryResolver();
104 for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) {
105 references.addAll(referenceIndex.getReferencesToMethod(methodEntry));
106 }
107
108 return references;
109 } else {
110 return referenceIndex.getReferencesToMethod(entry);
111 }
112 }
100} 113}
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
index 86655d09..ad3aceb0 100644
--- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
+++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java
@@ -12,11 +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.mapping.entry.ClassEntry; 15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import org.objectweb.asm.ClassReader; 16import org.objectweb.asm.ClassReader;
17import org.objectweb.asm.ClassVisitor; 17import org.objectweb.asm.ClassVisitor;
18import org.objectweb.asm.tree.ClassNode; 18import org.objectweb.asm.tree.ClassNode;
19 19
20import javax.annotation.Nullable;
20import java.io.BufferedInputStream; 21import java.io.BufferedInputStream;
21import java.io.IOException; 22import java.io.IOException;
22import java.io.InputStream; 23import java.io.InputStream;
@@ -100,9 +101,14 @@ public class ParsedJar {
100 return entries; 101 return entries;
101 } 102 }
102 103
104 @Nullable
103 public ClassNode getClassNode(String name) { 105 public ClassNode getClassNode(String name) {
104 return nodeCache.computeIfAbsent(name, (n) -> { 106 return nodeCache.computeIfAbsent(name, (n) -> {
105 ClassReader reader = new ClassReader(classBytes.get(name)); 107 byte[] bytes = classBytes.get(name);
108 if (bytes == null) {
109 return null;
110 }
111 ClassReader reader = new ClassReader(bytes);
106 ClassNode node = new ClassNode(); 112 ClassNode node = new ClassNode();
107 reader.accept(node, 0); 113 reader.accept(node, 0);
108 return node; 114 return node;
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
index 3950d165..c0a3a754 100644
--- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
+++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java
@@ -11,9 +11,9 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import cuchaz.enigma.mapping.entry.Entry; 14import cuchaz.enigma.translation.representation.entry.Entry;
15 15
16public interface ReferenceTreeNode<E extends Entry, C extends Entry> { 16public interface ReferenceTreeNode<E extends Entry<?>, C extends Entry<?>> {
17 E getEntry(); 17 E getEntry();
18 18
19 EntryReference<E, C> getReference(); 19 EntryReference<E, C> getReference();
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 3e0d66b4..abdec92e 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -11,17 +11,15 @@
11 11
12package cuchaz.enigma.analysis; 12package cuchaz.enigma.analysis;
13 13
14import com.google.common.collect.HashMultimap; 14import com.google.common.collect.*;
15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region; 15import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 16import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; 17import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
21import com.strobel.decompiler.languages.java.ast.Identifier; 18import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.TypeDeclaration; 19import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
23import cuchaz.enigma.mapping.entry.Entry; 20import cuchaz.enigma.translation.representation.entry.Entry;
24 21
22import javax.annotation.Nullable;
25import java.util.*; 23import java.util.*;
26import java.util.regex.Pattern; 24import java.util.regex.Pattern;
27 25
@@ -29,9 +27,9 @@ public class SourceIndex {
29 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); 27 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
30 28
31 private String source; 29 private String source;
32 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; 30 private TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReference;
33 private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; 31 private Multimap<EntryReference<Entry<?>, Entry<?>>, Token> referenceToTokens;
34 private Map<Entry, Token> declarationToToken; 32 private Map<Entry<?>, Token> declarationToToken;
35 private List<Integer> lineOffsets; 33 private List<Integer> lineOffsets;
36 private boolean ignoreBadTokens; 34 private boolean ignoreBadTokens;
37 35
@@ -42,7 +40,7 @@ public class SourceIndex {
42 public SourceIndex(String source, boolean ignoreBadTokens) { 40 public SourceIndex(String source, boolean ignoreBadTokens) {
43 this.source = source; 41 this.source = source;
44 this.ignoreBadTokens = ignoreBadTokens; 42 this.ignoreBadTokens = ignoreBadTokens;
45 this.tokenToReference = Maps.newTreeMap(); 43 this.tokenToReference = new TreeMap<>();
46 this.referenceToTokens = HashMultimap.create(); 44 this.referenceToTokens = HashMultimap.create();
47 this.declarationToToken = Maps.newHashMap(); 45 this.declarationToToken = Maps.newHashMap();
48 calculateLineOffsets(); 46 calculateLineOffsets();
@@ -63,12 +61,12 @@ public class SourceIndex {
63 this.source = source; 61 this.source = source;
64 calculateLineOffsets(); 62 calculateLineOffsets();
65 63
66 for (Entry entry : Lists.newArrayList(declarationToToken.keySet())) { 64 for (Entry<?> entry : Lists.newArrayList(declarationToToken.keySet())) {
67 Token token = declarationToToken.get(entry); 65 Token token = declarationToToken.get(entry);
68 declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); 66 declarationToToken.put(entry, tokenMap.getOrDefault(token, token));
69 } 67 }
70 68
71 for (EntryReference<Entry, Entry> ref : referenceToTokens.keySet()) { 69 for (EntryReference<Entry<?>, Entry<?>> ref : referenceToTokens.keySet()) {
72 Collection<Token> oldTokens = referenceToTokens.get(ref); 70 Collection<Token> oldTokens = referenceToTokens.get(ref);
73 List<Token> newTokens = new ArrayList<>(oldTokens.size()); 71 List<Token> newTokens = new ArrayList<>(oldTokens.size());
74 72
@@ -79,7 +77,8 @@ public class SourceIndex {
79 referenceToTokens.replaceValues(ref, newTokens); 77 referenceToTokens.replaceValues(ref, newTokens);
80 } 78 }
81 79
82 Map<Token, EntryReference<Entry, Entry>> tokenToReferenceCopy = Maps.newHashMap(tokenToReference); 80 TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReferenceCopy = new TreeMap<>(tokenToReference);
81
83 tokenToReference.clear(); 82 tokenToReference.clear();
84 for (Token token : tokenToReferenceCopy.keySet()) { 83 for (Token token : tokenToReferenceCopy.keySet()) {
85 tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); 84 tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token));
@@ -112,9 +111,9 @@ public class SourceIndex {
112 return null; 111 return null;
113 } 112 }
114 113
115 if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){ 114 if (node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()) {
116 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; 115 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
117 if (type != null){ 116 if (type != null) {
118 name = type.getName(); 117 name = type.getName();
119 token.end = token.start + name.length(); 118 token.end = token.start + name.length();
120 } 119 }
@@ -133,19 +132,19 @@ public class SourceIndex {
133 return token; 132 return token;
134 } 133 }
135 134
136 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { 135 public void addReference(AstNode node, Entry<?> deobfEntry, Entry<?> deobfContext) {
137 Token token = getToken(node); 136 Token token = getToken(node);
138 if (token != null) { 137 if (token != null) {
139 EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); 138 EntryReference<Entry<?>, Entry<?>> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext);
140 this.tokenToReference.put(token, deobfReference); 139 this.tokenToReference.put(token, deobfReference);
141 this.referenceToTokens.put(deobfReference, token); 140 this.referenceToTokens.put(deobfReference, token);
142 } 141 }
143 } 142 }
144 143
145 public void addDeclaration(AstNode node, Entry deobfEntry) { 144 public void addDeclaration(AstNode node, Entry<?> deobfEntry) {
146 Token token = getToken(node); 145 Token token = getToken(node);
147 if (token != null) { 146 if (token != null) {
148 EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); 147 EntryReference<Entry<?>, Entry<?>> reference = new EntryReference<>(deobfEntry, token.text);
149 this.tokenToReference.put(token, reference); 148 this.tokenToReference.put(token, reference);
150 this.referenceToTokens.put(reference, token); 149 this.referenceToTokens.put(reference, token);
151 this.declarationToToken.put(deobfEntry, token); 150 this.declarationToToken.put(deobfEntry, token);
@@ -160,22 +159,22 @@ public class SourceIndex {
160 return null; 159 return null;
161 } 160 }
162 161
163 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { 162 public Collection<Token> getReferenceTokens(EntryReference<Entry<?>, Entry<?>> deobfReference) {
164 return this.referenceToTokens.get(deobfReference); 163 return this.referenceToTokens.get(deobfReference);
165 } 164 }
166 165
167 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 166 @Nullable
167 public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) {
168 if (token == null) { 168 if (token == null) {
169 return null; 169 return null;
170 } 170 }
171 return this.tokenToReference.get(token); 171 return this.tokenToReference.get(token);
172 } 172 }
173 173
174 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { 174 public void replaceDeobfReference(Token token, EntryReference<Entry<?>, Entry<?>> newDeobfReference) {
175 EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); 175 EntryReference<Entry<?>, Entry<?>> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference);
176 this.tokenToReference.put(token, newDeobfReference); 176
177 Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); 177 Collection<Token> tokens = this.referenceToTokens.removeAll(oldDeobfReferences);
178 this.referenceToTokens.removeAll(oldDeobfReference);
179 this.referenceToTokens.putAll(newDeobfReference, tokens); 178 this.referenceToTokens.putAll(newDeobfReference, tokens);
180 } 179 }
181 180
@@ -187,11 +186,11 @@ public class SourceIndex {
187 return this.declarationToToken.values(); 186 return this.declarationToToken.values();
188 } 187 }
189 188
190 public Iterable<Entry> declarations() { 189 public Iterable<Entry<?>> declarations() {
191 return this.declarationToToken.keySet(); 190 return this.declarationToToken.keySet();
192 } 191 }
193 192
194 public Token getDeclarationToken(Entry deobfEntry) { 193 public Token getDeclarationToken(Entry<?> deobfEntry) {
195 return this.declarationToToken.get(deobfEntry); 194 return this.declarationToToken.get(deobfEntry);
196 } 195 }
197 196
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
index cad08574..486603ce 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java
@@ -17,9 +17,12 @@ import com.strobel.assembler.metadata.TypeDefinition;
17import com.strobel.assembler.metadata.TypeReference; 17import com.strobel.assembler.metadata.TypeReference;
18import com.strobel.decompiler.languages.TextLocation; 18import com.strobel.decompiler.languages.TextLocation;
19import com.strobel.decompiler.languages.java.ast.*; 19import com.strobel.decompiler.languages.java.ast.*;
20import cuchaz.enigma.bytecode.AccessFlags; 20import cuchaz.enigma.translation.representation.ProcyonEntryFactory;
21import cuchaz.enigma.mapping.Signature; 21import cuchaz.enigma.translation.representation.ReferencedEntryPool;
22import cuchaz.enigma.mapping.entry.*; 22import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
23import cuchaz.enigma.translation.representation.entry.ClassEntry;
24import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
25import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
23 26
24public class SourceIndexClassVisitor extends SourceIndexVisitor { 27public class SourceIndexClassVisitor extends SourceIndexVisitor {
25 private final ReferencedEntryPool entryPool; 28 private final ReferencedEntryPool entryPool;
@@ -37,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
37 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 40 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
38 // is this this class, or a subtype? 41 // is this this class, or a subtype?
39 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 42 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
40 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 43 ClassDefEntry classEntry = ClassDefEntry.parse(def);
41 if (!classEntry.equals(this.classEntry)) { 44 if (!classEntry.equals(this.classEntry)) {
42 // it's a subtype, recurse 45 // it's a subtype, recurse
43 index.addDeclaration(node.getNameToken(), classEntry); 46 index.addDeclaration(node.getNameToken(), classEntry);
@@ -68,7 +71,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
68 tokenNode = node.getModifiers().firstOrNullObject(); 71 tokenNode = node.getModifiers().firstOrNullObject();
69 } 72 }
70 index.addDeclaration(tokenNode, methodEntry); 73 index.addDeclaration(tokenNode, methodEntry);
71 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); 74 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index);
72 } 75 }
73 76
74 @Override 77 @Override
@@ -76,7 +79,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor {
76 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); 79 MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION);
77 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); 80 MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def);
78 index.addDeclaration(node.getNameToken(), methodEntry); 81 index.addDeclaration(node.getNameToken(), methodEntry);
79 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, classEntry, methodEntry), index); 82 return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index);
80 } 83 }
81 84
82 @Override 85 @Override
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 139fceac..73db28fb 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -17,8 +17,9 @@ 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.mapping.TypeDescriptor; 20import cuchaz.enigma.translation.representation.TypeDescriptor;
21import cuchaz.enigma.mapping.entry.*; 21import cuchaz.enigma.translation.representation.*;
22import cuchaz.enigma.translation.representation.entry.*;
22 23
23import java.lang.Error; 24import java.lang.Error;
24import java.util.HashMap; 25import java.util.HashMap;
@@ -26,19 +27,15 @@ import java.util.Map;
26 27
27public class SourceIndexMethodVisitor extends SourceIndexVisitor { 28public class SourceIndexMethodVisitor extends SourceIndexVisitor {
28 private final ReferencedEntryPool entryPool; 29 private final ReferencedEntryPool entryPool;
29 private final ProcyonEntryFactory entryFactory;
30 30
31 private final ClassDefEntry ownerEntry;
32 private final MethodDefEntry methodEntry; 31 private final MethodDefEntry methodEntry;
33 32
34 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); 33 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
35 private Map<String, Entry> identifierEntryCache = new HashMap<>(); 34 private Map<String, Entry<?>> identifierEntryCache = new HashMap<>();
36 35
37 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, ClassDefEntry ownerEntry, MethodDefEntry methodEntry) { 36 public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) {
38 super(entryPool); 37 super(entryPool);
39 this.entryPool = entryPool; 38 this.entryPool = entryPool;
40 this.entryFactory = new ProcyonEntryFactory(entryPool);
41 this.ownerEntry = ownerEntry;
42 this.methodEntry = methodEntry; 39 this.methodEntry = methodEntry;
43 } 40 }
44 41
@@ -86,7 +83,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
86 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 83 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
87 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); 84 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature));
88 if (fieldEntry == null) { 85 if (fieldEntry == null) {
89 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); 86 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
90 } 87 }
91 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); 88 index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry);
92 } 89 }
@@ -128,7 +125,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
128 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); 125 ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName());
129 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); 126 FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature()));
130 if (fieldEntry == null) { 127 if (fieldEntry == null) {
131 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getName()); 128 throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName());
132 } 129 }
133 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); 130 index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry);
134 } else 131 } else
@@ -144,7 +141,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
144 } 141 }
145 142
146 private void addDeclarationToUnmatched(String key, SourceIndex index) { 143 private void addDeclarationToUnmatched(String key, SourceIndex index) {
147 Entry entry = identifierEntryCache.get(key); 144 Entry<?> entry = identifierEntryCache.get(key);
148 145
149 // This cannot happened in theory 146 // This cannot happened in theory
150 if (entry == null) 147 if (entry == null)
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
index e588d24b..564830c6 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java
@@ -14,10 +14,8 @@ 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.bytecode.AccessFlags; 17import cuchaz.enigma.translation.representation.ReferencedEntryPool;
18import cuchaz.enigma.mapping.Signature; 18import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.mapping.entry.ClassDefEntry;
20import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
21 19
22public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { 20public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
23 private final ReferencedEntryPool entryPool; 21 private final ReferencedEntryPool entryPool;
@@ -29,7 +27,7 @@ public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> {
29 @Override 27 @Override
30 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { 28 public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) {
31 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); 29 TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION);
32 ClassDefEntry classEntry = new ClassDefEntry(def.getInternalName(), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); 30 ClassDefEntry classEntry = ClassDefEntry.parse(def);
33 index.addDeclaration(node.getNameToken(), classEntry); 31 index.addDeclaration(node.getNameToken(), classEntry);
34 32
35 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); 33 return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index);
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
deleted file mode 100644
index 984d84b2..00000000
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ /dev/null
@@ -1,275 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis;
13
14import com.google.common.cache.Cache;
15import com.google.common.cache.CacheBuilder;
16import com.google.common.collect.HashMultimap;
17import com.google.common.collect.Lists;
18import com.google.common.collect.Maps;
19import com.google.common.collect.Multimap;
20import cuchaz.enigma.bytecode.AccessFlags;
21import cuchaz.enigma.mapping.*;
22import cuchaz.enigma.mapping.entry.*;
23
24import java.util.*;
25
26public class TranslationIndex {
27
28 private final ReferencedEntryPool entryPool;
29 private Map<ClassEntry, ClassEntry> superclasses;
30 private Map<Entry, DefEntry> defEntries = new HashMap<>();
31 private Multimap<ClassEntry, FieldDefEntry> fieldEntries;
32 private Multimap<ClassEntry, MethodDefEntry> methodEntries;
33 private Multimap<ClassEntry, ClassEntry> interfaces;
34
35 public TranslationIndex(ReferencedEntryPool entryPool) {
36 this.entryPool = entryPool;
37 this.superclasses = Maps.newHashMap();
38 this.fieldEntries = HashMultimap.create();
39 this.methodEntries = HashMultimap.create();
40 this.interfaces = HashMultimap.create();
41
42 for (FieldDefEntry entry : fieldEntries.values()) {
43 defEntries.put(entry, entry);
44 }
45
46 for (MethodDefEntry entry : methodEntries.values()) {
47 defEntries.put(entry, entry);
48 }
49 }
50
51 public TranslationIndex(TranslationIndex other, Translator translator) {
52 this.entryPool = other.entryPool;
53
54 // translate the superclasses
55 this.superclasses = Maps.newHashMap();
56 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) {
57 this.superclasses.put(translator.getTranslatedClass(mapEntry.getKey()), translator.getTranslatedClass(mapEntry.getValue()));
58 }
59
60 // translate the interfaces
61 this.interfaces = HashMultimap.create();
62 for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) {
63 this.interfaces.put(
64 translator.getTranslatedClass(mapEntry.getKey()),
65 translator.getTranslatedClass(mapEntry.getValue())
66 );
67 }
68
69 // translate the fields
70 this.fieldEntries = HashMultimap.create();
71 for (Map.Entry<ClassEntry, FieldDefEntry> mapEntry : other.fieldEntries.entries()) {
72 this.fieldEntries.put(
73 translator.getTranslatedClass(mapEntry.getKey()),
74 translator.getTranslatedFieldDef(mapEntry.getValue())
75 );
76 }
77
78 this.methodEntries = HashMultimap.create();
79 for (Map.Entry<ClassEntry, MethodDefEntry> mapEntry : other.methodEntries.entries()) {
80 this.methodEntries.put(
81 translator.getTranslatedClass(mapEntry.getKey()),
82 translator.getTranslatedMethodDef(mapEntry.getValue())
83 );
84 }
85
86 for (FieldDefEntry entry : fieldEntries.values()) {
87 defEntries.put(entry, entry);
88 }
89
90 for (MethodDefEntry entry : methodEntries.values()) {
91 defEntries.put(entry, entry);
92 }
93 }
94
95 protected ClassDefEntry indexClass(int access, String name, String signature, String superName, String[] interfaces) {
96 ClassDefEntry classEntry = new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access));
97 if (isJre(classEntry)) {
98 return null;
99 }
100
101 // add the superclass
102 ClassEntry superclassEntry = entryPool.getClass(superName);
103 if (superclassEntry != null) {
104 this.superclasses.put(classEntry, superclassEntry);
105 }
106
107 // add the interfaces
108 for (String interfaceClassName : interfaces) {
109 ClassEntry interfaceClassEntry = entryPool.getClass(interfaceClassName);
110 if (!isJre(interfaceClassEntry)) {
111 this.interfaces.put(classEntry, interfaceClassEntry);
112 }
113 }
114
115 return classEntry;
116 }
117
118 protected void indexField(FieldDefEntry fieldEntry) {
119 this.fieldEntries.put(fieldEntry.getOwnerClassEntry(), fieldEntry);
120 this.defEntries.put(fieldEntry, fieldEntry);
121 }
122
123 protected void indexMethod(MethodDefEntry methodEntry) {
124 this.methodEntries.put(methodEntry.getOwnerClassEntry(), methodEntry);
125 this.defEntries.put(methodEntry, methodEntry);
126 }
127
128 public void renameClasses(Map<String, String> renames) {
129 EntryRenamer.renameClassesInMap(renames, this.superclasses);
130 EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries);
131 EntryRenamer.renameClassesInMultimap(renames, this.methodEntries);
132
133 this.defEntries.clear();
134 for (FieldDefEntry entry : fieldEntries.values()) {
135 defEntries.put(entry, entry);
136 }
137
138 for (MethodDefEntry entry : methodEntries.values()) {
139 defEntries.put(entry, entry);
140 }
141 }
142
143 public ClassEntry getSuperclass(ClassEntry classEntry) {
144 return this.superclasses.get(classEntry);
145 }
146
147 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
148 List<ClassEntry> ancestors = Lists.newArrayList();
149 while (classEntry != null) {
150 classEntry = getSuperclass(classEntry);
151 if (classEntry != null) {
152 ancestors.add(classEntry);
153 }
154 }
155 return ancestors;
156 }
157
158 public List<ClassEntry> getImplementers(ClassEntry classEntry) {
159 // linear search is fast enough for now
160 List<ClassEntry> implementers = Lists.newArrayList();
161 for (ClassEntry itf : this.interfaces.keySet()) {
162 if (this.interfaces.containsEntry(itf, classEntry)) {
163 implementers.add(itf);
164 }
165 }
166 return implementers;
167 }
168
169 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
170 // linear search is fast enough for now
171 List<ClassEntry> subclasses = Lists.newArrayList();
172 for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) {
173 ClassEntry subclass = entry.getKey();
174 ClassEntry superclass = entry.getValue();
175 if (classEntry.equals(superclass)) {
176 subclasses.add(subclass);
177 }
178 }
179 return subclasses;
180 }
181
182 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
183 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
184 out.add(subclassEntry);
185 getSubclassesRecursively(out, subclassEntry);
186 }
187 }
188
189 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
190 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
191 out.add(subclassEntry.getName());
192 getSubclassNamesRecursively(out, subclassEntry);
193 }
194 }
195
196 public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() {
197 return this.interfaces.entries();
198 }
199
200 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
201 return this.interfaces.get(classEntry);
202 }
203
204 public boolean isInterface(ClassEntry classEntry) {
205 return this.interfaces.containsValue(classEntry);
206 }
207
208 public boolean entryExists(Entry entry) {
209 if (entry == null) {
210 return false;
211 }
212
213 if (entry instanceof FieldEntry) {
214 return fieldExists((FieldEntry) entry);
215 } else if (entry instanceof MethodEntry) {
216 return methodExists((MethodEntry) entry);
217 } else if (entry instanceof LocalVariableEntry) {
218 return methodExists(((LocalVariableEntry) entry).getOwnerEntry());
219 }
220 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
221 }
222
223 public boolean fieldExists(FieldEntry fieldEntry) {
224 return this.fieldEntries.containsEntry(fieldEntry.getOwnerClassEntry(), fieldEntry);
225 }
226
227 public boolean methodExists(MethodEntry methodEntry) {
228 return this.methodEntries.containsEntry(methodEntry.getOwnerClassEntry(), methodEntry);
229 }
230
231 public ClassEntry resolveEntryOwner(Entry entry) {
232 if (entry instanceof ClassEntry) {
233 return (ClassEntry) entry;
234 }
235
236 if (entryExists(entry)) {
237 return entry.getOwnerClassEntry();
238 }
239
240 DefEntry def = defEntries.get(entry);
241 if (def != null && (def.getAccess().isPrivate())) {
242 return null;
243 }
244
245 // if we're protected/public/non-static, chances are we're somewhere down
246 LinkedList<ClassEntry> classEntries = new LinkedList<>();
247 classEntries.add(entry.getOwnerClassEntry());
248 while (!classEntries.isEmpty()) {
249 ClassEntry c = classEntries.remove();
250 Entry cEntry = entry.updateOwnership(c);
251
252 if (entryExists(cEntry)) {
253 def = defEntries.get(cEntry);
254 if (def == null || (!def.getAccess().isPrivate())) {
255 return cEntry.getOwnerClassEntry();
256 }
257 }
258
259 ClassEntry superC = getSuperclass(c);
260 if (superC != null) {
261 classEntries.add(superC);
262 }
263 if (entry instanceof MethodEntry) {
264 classEntries.addAll(getInterfaces(c));
265 }
266 }
267
268 return null;
269 }
270
271 private boolean isJre(ClassEntry classEntry) {
272 String packageName = classEntry.getPackageName();
273 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
274 }
275}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
new file mode 100644
index 00000000..e1903d9f
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -0,0 +1,77 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.Maps;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryResolver;
6import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.entry.MethodEntry;
8
9import javax.annotation.Nullable;
10import java.util.Collection;
11import java.util.Map;
12
13public class BridgeMethodIndex implements JarIndexer, RemappableIndex {
14 private final EntryIndex entryIndex;
15 private final ReferenceIndex referenceIndex;
16
17 private Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap();
18
19 public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) {
20 this.entryIndex = entryIndex;
21 this.referenceIndex = referenceIndex;
22 }
23
24 @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) {
39 // look for access and bridged methods
40 for (MethodEntry methodEntry : entryIndex.getMethods()) {
41 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
42 if (access == null || !access.isSynthetic()) {
43 continue;
44 }
45
46 indexSyntheticMethod(methodEntry, access);
47 }
48 }
49
50 private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) {
51 if (access.isBridge()) {
52 MethodEntry accessedMethod = findAccessMethod(syntheticMethod);
53 if (accessedMethod != null) {
54 accessedToBridge.put(accessedMethod, syntheticMethod);
55 }
56 }
57 }
58
59 private MethodEntry findAccessMethod(MethodEntry method) {
60 // we want to find all compiler-added methods that directly call another with no processing
61
62 // get all the methods that we call
63 final Collection<MethodEntry> referencedMethods = referenceIndex.getMethodsReferencedBy(method);
64
65 // is there just one?
66 if (referencedMethods.size() != 1) {
67 return null;
68 }
69
70 return referencedMethods.stream().findFirst().orElse(null);
71 }
72
73 @Nullable
74 public MethodEntry getBridgeFromAccessed(MethodEntry entry) {
75 return accessedToBridge.get(entry);
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
new file mode 100644
index 00000000..55bfbc24
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java
@@ -0,0 +1,109 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.AccessFlags;
5import cuchaz.enigma.translation.representation.entry.*;
6
7import javax.annotation.Nullable;
8import java.util.Collection;
9import java.util.HashMap;
10import java.util.Map;
11
12public class EntryIndex implements JarIndexer, RemappableIndex {
13 private Map<ClassEntry, AccessFlags> classes = new HashMap<>();
14 private Map<FieldEntry, AccessFlags> fields = new HashMap<>();
15 private Map<MethodEntry, AccessFlags> methods = new HashMap<>();
16
17 @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) {
36 classes.put(classEntry, classEntry.getAccess());
37 }
38
39 @Override
40 public void indexMethod(MethodDefEntry methodEntry) {
41 methods.put(methodEntry, methodEntry.getAccess());
42 }
43
44 @Override
45 public void indexField(FieldDefEntry fieldEntry) {
46 fields.put(fieldEntry, fieldEntry.getAccess());
47 }
48
49 public boolean hasClass(ClassEntry entry) {
50 return classes.containsKey(entry);
51 }
52
53 public boolean hasMethod(MethodEntry entry) {
54 return methods.containsKey(entry);
55 }
56
57 public boolean hasField(FieldEntry entry) {
58 return fields.containsKey(entry);
59 }
60
61 public boolean hasEntry(Entry<?> entry) {
62 if (entry instanceof ClassEntry) {
63 return hasClass((ClassEntry) entry);
64 } else if (entry instanceof MethodEntry) {
65 return hasMethod((MethodEntry) entry);
66 } else if (entry instanceof FieldEntry) {
67 return hasField((FieldEntry) entry);
68 } else if (entry instanceof LocalVariableEntry) {
69 return hasMethod(((LocalVariableEntry) entry).getParent());
70 }
71
72 return false;
73 }
74
75 @Nullable
76 public AccessFlags getMethodAccess(MethodEntry entry) {
77 return methods.get(entry);
78 }
79
80 @Nullable
81 public AccessFlags getFieldAccess(FieldEntry entry) {
82 return fields.get(entry);
83 }
84
85 @Nullable
86 public AccessFlags getEntryAccess(Entry<?> entry) {
87 if (entry instanceof MethodEntry) {
88 return getMethodAccess((MethodEntry) entry);
89 } else if (entry instanceof FieldEntry) {
90 return getFieldAccess((FieldEntry) entry);
91 } else if (entry instanceof LocalVariableEntry) {
92 return getMethodAccess(((LocalVariableEntry) entry).getParent());
93 }
94
95 return null;
96 }
97
98 public Collection<ClassEntry> getClasses() {
99 return classes.keySet();
100 }
101
102 public Collection<MethodEntry> getMethods() {
103 return methods.keySet();
104 }
105
106 public Collection<FieldEntry> getFields() {
107 return fields.keySet();
108 }
109}
diff --git a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
index 4d5e8037..f9cb23ce 100644
--- a/src/main/java/cuchaz/enigma/analysis/IndexClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java
@@ -1,43 +1,40 @@
1package cuchaz.enigma.analysis; 1package cuchaz.enigma.analysis.index;
2 2
3import cuchaz.enigma.mapping.entry.ClassDefEntry; 3import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
4import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
5import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
4import org.objectweb.asm.ClassVisitor; 6import org.objectweb.asm.ClassVisitor;
5import org.objectweb.asm.FieldVisitor; 7import org.objectweb.asm.FieldVisitor;
6import org.objectweb.asm.MethodVisitor; 8import org.objectweb.asm.MethodVisitor;
7 9
8public class IndexClassVisitor extends ClassVisitor { 10public class IndexClassVisitor extends ClassVisitor {
9 private final JarIndex index; 11 private final JarIndexer indexer;
10 private ClassDefEntry classEntry; 12 private ClassDefEntry classEntry;
11 13
12 public IndexClassVisitor(JarIndex index, int api) { 14 public IndexClassVisitor(JarIndex indexer, int api) {
13 super(api); 15 super(api);
14 this.index = index; 16 this.indexer = indexer;
15 }
16
17 public IndexClassVisitor(JarIndex index, int api, ClassVisitor cv) {
18 super(api, cv);
19 this.index = index;
20 } 17 }
21 18
22 @Override 19 @Override
23 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 20 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 this.classEntry = this.index.indexClass(access, name, signature, superName, interfaces); 21 classEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
22 indexer.indexClass(classEntry);
23
25 super.visit(version, access, name, signature, superName, interfaces); 24 super.visit(version, access, name, signature, superName, interfaces);
26 } 25 }
27 26
28 @Override 27 @Override
29 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 28 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
30 if (this.classEntry != null) { 29 indexer.indexField(FieldDefEntry.parse(classEntry, access, name, desc, signature));
31 this.index.indexField(this.classEntry, access, name, desc, signature); 30
32 }
33 return super.visitField(access, name, desc, signature, value); 31 return super.visitField(access, name, desc, signature, value);
34 } 32 }
35 33
36 @Override 34 @Override
37 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 35 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
38 if (this.classEntry != null) { 36 indexer.indexMethod(MethodDefEntry.parse(classEntry, access, name, desc, signature));
39 this.index.indexMethod(this.classEntry, access, name, desc, signature); 37
40 }
41 return super.visitMethod(access, name, desc, signature, exceptions); 38 return super.visitMethod(access, name, desc, signature, exceptions);
42 } 39 }
43} 40}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
new file mode 100644
index 00000000..ba5d3b6a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.Signature;
6import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.FieldEntry;
8import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
9import cuchaz.enigma.translation.representation.entry.MethodEntry;
10import org.objectweb.asm.ClassVisitor;
11import org.objectweb.asm.Handle;
12import org.objectweb.asm.MethodVisitor;
13import org.objectweb.asm.Opcodes;
14
15public class IndexReferenceVisitor extends ClassVisitor {
16 private final JarIndexer indexer;
17 private ClassEntry classEntry;
18
19 public IndexReferenceVisitor(JarIndexer indexer, int api) {
20 super(api);
21 this.indexer = indexer;
22 }
23
24 @Override
25 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
26 this.classEntry = new ClassEntry(name);
27 }
28
29 @Override
30 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 MethodDefEntry entry = new MethodDefEntry(classEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
32 return new Method(this.indexer, entry, this.api);
33 }
34
35 private static class Method extends MethodVisitor {
36 private final JarIndexer indexer;
37 private final MethodDefEntry callerEntry;
38
39 public Method(JarIndexer indexer, MethodDefEntry callerEntry, int api) {
40 super(api);
41 this.indexer = indexer;
42 this.callerEntry = callerEntry;
43 }
44
45 @Override
46 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
47 FieldEntry fieldEntry = FieldEntry.parse(owner, name, desc);
48 this.indexer.indexFieldReference(callerEntry, fieldEntry);
49 }
50
51 @Override
52 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
53 MethodEntry methodEntry = MethodEntry.parse(owner, name, desc);
54 this.indexer.indexMethodReference(callerEntry, methodEntry);
55 }
56
57 @Override
58 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
59 for (Object bsmArg : bsmArgs) {
60 if (bsmArg instanceof Handle) {
61 Handle handle = (Handle) bsmArg;
62 switch (handle.getTag()) {
63 case Opcodes.H_GETFIELD:
64 case Opcodes.H_GETSTATIC:
65 case Opcodes.H_PUTFIELD:
66 case Opcodes.H_PUTSTATIC:
67 FieldEntry fieldEntry = FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
68 this.indexer.indexFieldReference(callerEntry, fieldEntry);
69 break;
70 case Opcodes.H_INVOKEINTERFACE:
71 case Opcodes.H_INVOKESPECIAL:
72 case Opcodes.H_INVOKESTATIC:
73 case Opcodes.H_INVOKEVIRTUAL:
74 case Opcodes.H_NEWINVOKESPECIAL:
75 MethodEntry methodEntry = MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc());
76 this.indexer.indexMethodReference(callerEntry, methodEntry);
77 break;
78 }
79 }
80 }
81 }
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
new file mode 100644
index 00000000..d165cc83
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -0,0 +1,97 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20
21import java.util.Collection;
22import java.util.LinkedList;
23import java.util.Set;
24
25public class InheritanceIndex implements JarIndexer, RemappableIndex {
26 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
27 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
28
29 @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) {
46 ClassEntry superClass = classEntry.getSuperClass();
47 if (superClass != null) {
48 indexParent(classEntry, superClass);
49 }
50
51 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
52 indexParent(classEntry, interfaceEntry);
53 }
54 }
55
56 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) {
57 if (childEntry.isJre() || parentEntry.isJre()) {
58 return;
59 }
60 classParents.put(childEntry, parentEntry);
61 classChildren.put(parentEntry, childEntry);
62 }
63
64 public Collection<ClassEntry> getParents(ClassEntry classEntry) {
65 return classParents.get(classEntry);
66 }
67
68 public Collection<ClassEntry> getChildren(ClassEntry classEntry) {
69 return classChildren.get(classEntry);
70 }
71
72 public Set<ClassEntry> getAncestors(ClassEntry classEntry) {
73 Set<ClassEntry> ancestors = Sets.newHashSet();
74
75 LinkedList<ClassEntry> ancestorQueue = new LinkedList<>();
76 ancestorQueue.push(classEntry);
77
78 while (!ancestorQueue.isEmpty()) {
79 ClassEntry ancestor = ancestorQueue.pop();
80 Collection<ClassEntry> parents = getParents(ancestor);
81
82 parents.forEach(ancestorQueue::push);
83 ancestors.addAll(parents);
84 }
85
86 return ancestors;
87 }
88
89 public boolean isParent(ClassEntry classEntry) {
90 return classChildren.containsKey(classEntry);
91 }
92
93 public boolean hasParents(ClassEntry classEntry) {
94 Collection<ClassEntry> parents = classParents.get(classEntry);
95 return parents != null && !parents.isEmpty();
96 }
97}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
new file mode 100644
index 00000000..0880244a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -0,0 +1,165 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.analysis.index;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.IndexEntryResolver;
20import cuchaz.enigma.translation.representation.entry.*;
21import org.objectweb.asm.ClassReader;
22import org.objectweb.asm.Opcodes;
23
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.function.Consumer;
27
28public class JarIndex implements JarIndexer, RemappableIndex {
29 private final EntryIndex entryIndex;
30 private final InheritanceIndex inheritanceIndex;
31 private final ReferenceIndex referenceIndex;
32 private final BridgeMethodIndex bridgeMethodIndex;
33 private final EntryResolver entryResolver;
34
35 private final Collection<JarIndexer> indexers;
36
37 private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create();
38
39 public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex) {
40 this.entryIndex = entryIndex;
41 this.inheritanceIndex = inheritanceIndex;
42 this.referenceIndex = referenceIndex;
43 this.bridgeMethodIndex = bridgeMethodIndex;
44 this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
45 this.entryResolver = new IndexEntryResolver(this);
46 }
47
48 public static JarIndex empty() {
49 EntryIndex entryIndex = new EntryIndex();
50 InheritanceIndex inheritanceIndex = new InheritanceIndex();
51 ReferenceIndex referenceIndex = new ReferenceIndex();
52 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex);
53 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
54 }
55
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) {
76 progress.accept("Indexing entries (1/3)");
77 jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE);
78
79 progress.accept("Indexing entry references (2/3)");
80 jar.visitReader(name -> new IndexReferenceVisitor(this, Opcodes.ASM5), ClassReader.SKIP_FRAMES);
81
82 progress.accept("Processing index (3/3)");
83 processIndex(entryResolver);
84 }
85
86 @Override
87 public void processIndex(EntryResolver resolver) {
88 indexers.forEach(indexer -> indexer.processIndex(entryResolver));
89 }
90
91 @Override
92 public void indexClass(ClassDefEntry classEntry) {
93 if (classEntry.isJre()) {
94 return;
95 }
96
97 for (ClassEntry interfaceEntry : classEntry.getInterfaces()) {
98 if (classEntry.equals(interfaceEntry)) {
99 throw new IllegalArgumentException("Class cannot be its own interface! " + classEntry);
100 }
101 }
102
103 indexers.forEach(indexer -> indexer.indexClass(classEntry));
104 }
105
106 @Override
107 public void indexField(FieldDefEntry fieldEntry) {
108 if (fieldEntry.getParent().isJre()) {
109 return;
110 }
111
112 indexers.forEach(indexer -> indexer.indexField(fieldEntry));
113 }
114
115 @Override
116 public void indexMethod(MethodDefEntry methodEntry) {
117 if (methodEntry.getParent().isJre()) {
118 return;
119 }
120
121 indexers.forEach(indexer -> indexer.indexMethod(methodEntry));
122
123 if (!methodEntry.isConstructor()) {
124 methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry);
125 }
126 }
127
128 @Override
129 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
130 if (callerEntry.getParent().isJre()) {
131 return;
132 }
133
134 indexers.forEach(indexer -> indexer.indexMethodReference(callerEntry, referencedEntry));
135 }
136
137 @Override
138 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
139 if (callerEntry.getParent().isJre()) {
140 return;
141 }
142
143 indexers.forEach(indexer -> indexer.indexFieldReference(callerEntry, referencedEntry));
144 }
145
146 public EntryIndex getEntryIndex() {
147 return entryIndex;
148 }
149
150 public InheritanceIndex getInheritanceIndex() {
151 return this.inheritanceIndex;
152 }
153
154 public ReferenceIndex getReferenceIndex() {
155 return referenceIndex;
156 }
157
158 public BridgeMethodIndex getBridgeMethodIndex() {
159 return bridgeMethodIndex;
160 }
161
162 public EntryResolver getEntryResolver() {
163 return entryResolver;
164 }
165}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
new file mode 100644
index 00000000..a087e598
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java
@@ -0,0 +1,24 @@
1package cuchaz.enigma.analysis.index;
2
3import cuchaz.enigma.translation.mapping.EntryResolver;
4import cuchaz.enigma.translation.representation.entry.*;
5
6public interface JarIndexer {
7 default void indexClass(ClassDefEntry classEntry) {
8 }
9
10 default void indexField(FieldDefEntry fieldEntry) {
11 }
12
13 default void indexMethod(MethodDefEntry methodEntry) {
14 }
15
16 default void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
17 }
18
19 default void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
20 }
21
22 default void processIndex(EntryResolver resolver) {
23 }
24}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
new file mode 100644
index 00000000..ac11da46
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.analysis.index;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Multimap;
5import cuchaz.enigma.analysis.EntryReference;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.ResolutionStrategy;
8import cuchaz.enigma.translation.representation.entry.*;
9
10import java.util.Collection;
11import java.util.Map;
12
13public class ReferenceIndex implements JarIndexer {
14 private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create();
15
16 private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create();
17 private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create();
18 private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create();
19
20 @Override
21 public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
22 referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
23 methodReferences.put(callerEntry, referencedEntry);
24
25 if (referencedEntry.isConstructor()) {
26 ClassEntry referencedClass = referencedEntry.getParent();
27 referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry));
28 }
29 }
30
31 @Override
32 public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
33 referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
34 }
35
36 @Override
37 public void processIndex(EntryResolver resolver) {
38 methodReferences = resolveReferences(resolver, methodReferences);
39 referencesToMethods = resolveReferencesTo(resolver, referencesToMethods);
40 referencesToClasses = resolveReferencesTo(resolver, referencesToClasses);
41 referencesToFields = resolveReferencesTo(resolver, referencesToFields);
42 }
43
44 private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> resolveReferences(EntryResolver resolver, Multimap<K, V> multimap) {
45 Multimap<K, V> resolved = HashMultimap.create();
46 for (Map.Entry<K, V> entry : multimap.entries()) {
47 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
48 }
49 return resolved;
50 }
51
52 private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> resolveReferencesTo(EntryResolver resolver, Multimap<E, EntryReference<E, C>> multimap) {
53 Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create();
54 for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) {
55 resolved.put(resolve(resolver, entry.getKey()), resolve(resolver, entry.getValue()));
56 }
57 return resolved;
58 }
59
60 private <E extends Entry<?>> E resolve(EntryResolver resolver, E entry) {
61 return resolver.resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST);
62 }
63
64 private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> resolve(EntryResolver resolver, EntryReference<E, C> reference) {
65 return resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);
66 }
67
68 public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) {
69 return methodReferences.get(entry);
70 }
71
72 public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) {
73 return referencesToFields.get(entry);
74 }
75
76 public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) {
77 return referencesToClasses.get(entry);
78 }
79
80 public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) {
81 return referencesToMethods.get(entry);
82 }
83}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
new file mode 100644
index 00000000..537e7726
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java
@@ -0,0 +1,9 @@
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/AsmObjectTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java
new file mode 100644
index 00000000..1a2b47fb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java
@@ -0,0 +1,46 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.representation.MethodDescriptor;
5import cuchaz.enigma.translation.representation.TypeDescriptor;
6import cuchaz.enigma.translation.representation.entry.ClassEntry;
7import cuchaz.enigma.translation.representation.entry.MethodEntry;
8import org.objectweb.asm.Handle;
9import org.objectweb.asm.Type;
10
11public class AsmObjectTranslator {
12 public static Type translateType(Translator translator, Type type) {
13 String descString = type.getDescriptor();
14 switch (type.getSort()) {
15 case Type.OBJECT: {
16 ClassEntry classEntry = new ClassEntry(type.getInternalName());
17 return Type.getObjectType(translator.translate(classEntry).getFullName());
18 }
19 case Type.ARRAY: {
20 TypeDescriptor descriptor = new TypeDescriptor(descString);
21 return Type.getType(translator.translate(descriptor).toString());
22 }
23 case Type.METHOD: {
24 MethodDescriptor descriptor = new MethodDescriptor(descString);
25 return Type.getMethodType(translator.translate(descriptor).toString());
26 }
27 }
28 return type;
29 }
30
31 public static Handle translateHandle(Translator translator, Handle handle) {
32 MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc()));
33 MethodEntry translatedMethod = translator.translate(entry);
34 ClassEntry ownerClass = translatedMethod.getParent();
35 return new Handle(handle.getTag(), ownerClass.getFullName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface());
36 }
37
38 public static Object translateValue(Translator translator, Object value) {
39 if (value instanceof Type) {
40 return translateType(translator, (Type) value);
41 } else if (value instanceof Handle) {
42 return translateHandle(translator, (Handle) value);
43 }
44 return value;
45 }
46}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
index 2e5b54d9..cb843ad4 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java
@@ -1,9 +1,9 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.mapping.Translator; 3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor; 4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.ClassEntry; 5import cuchaz.enigma.translation.representation.entry.ClassEntry;
6import cuchaz.enigma.mapping.entry.FieldEntry; 6import cuchaz.enigma.translation.representation.entry.FieldEntry;
7import org.objectweb.asm.AnnotationVisitor; 7import org.objectweb.asm.AnnotationVisitor;
8 8
9public class TranslationAnnotationVisitor extends AnnotationVisitor { 9public class TranslationAnnotationVisitor extends AnnotationVisitor {
@@ -18,7 +18,7 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor {
18 18
19 @Override 19 @Override
20 public void visit(String name, Object value) { 20 public void visit(String name, Object value) {
21 super.visit(name, translator.getTranslatedValue(value)); 21 super.visit(name, AsmObjectTranslator.translateValue(translator, value));
22 } 22 }
23 23
24 @Override 24 @Override
@@ -30,22 +30,22 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor {
30 public AnnotationVisitor visitAnnotation(String name, String desc) { 30 public AnnotationVisitor visitAnnotation(String name, String desc) {
31 TypeDescriptor type = new TypeDescriptor(desc); 31 TypeDescriptor type = new TypeDescriptor(desc);
32 if (name != null) { 32 if (name != null) {
33 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type)); 33 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type));
34 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString()); 34 return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString());
35 } else { 35 } else {
36 return super.visitAnnotation(null, translator.getTranslatedTypeDesc(type).toString()); 36 return super.visitAnnotation(null, translator.translate(type).toString());
37 } 37 }
38 } 38 }
39 39
40 @Override 40 @Override
41 public void visitEnum(String name, String desc, String value) { 41 public void visitEnum(String name, String desc, String value) {
42 TypeDescriptor type = new TypeDescriptor(desc); 42 TypeDescriptor type = new TypeDescriptor(desc);
43 FieldEntry enumField = translator.getTranslatedField(new FieldEntry(type.getTypeEntry(), value, type)); 43 FieldEntry enumField = translator.translate(new FieldEntry(type.getTypeEntry(), value, type));
44 if (name != null) { 44 if (name != null) {
45 FieldEntry annotationField = translator.getTranslatedField(new FieldEntry(annotationEntry, name, type)); 45 FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type));
46 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName()); 46 super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName());
47 } else { 47 } else {
48 super.visitEnum(null, translator.getTranslatedTypeDesc(type).toString(), enumField.getName()); 48 super.visitEnum(null, translator.translate(type).toString(), enumField.getName());
49 } 49 }
50 } 50 }
51} 51}
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
index 5b16138e..53d09bb6 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java
@@ -11,61 +11,53 @@
11 11
12package cuchaz.enigma.bytecode.translators; 12package cuchaz.enigma.bytecode.translators;
13 13
14import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.translation.Translator;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.translation.representation.MethodDescriptor;
16import cuchaz.enigma.mapping.MethodDescriptor; 16import cuchaz.enigma.translation.representation.ReferencedEntryPool;
17import cuchaz.enigma.mapping.Signature; 17import cuchaz.enigma.translation.representation.TypeDescriptor;
18import cuchaz.enigma.mapping.Translator; 18import cuchaz.enigma.translation.representation.entry.*;
19import cuchaz.enigma.mapping.TypeDescriptor;
20import cuchaz.enigma.mapping.entry.*;
21import org.objectweb.asm.*; 19import org.objectweb.asm.*;
22 20
21import java.util.Arrays;
22
23public class TranslationClassVisitor extends ClassVisitor { 23public class TranslationClassVisitor extends ClassVisitor {
24 private final Translator translator; 24 private final Translator translator;
25 private final JarIndex jarIndex;
26 private final ReferencedEntryPool entryPool; 25 private final ReferencedEntryPool entryPool;
27 26
28 private ClassDefEntry obfClassEntry; 27 private ClassDefEntry obfClassEntry;
29 private Signature obfSignature;
30 28
31 public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) { 29 public TranslationClassVisitor(Translator translator, ReferencedEntryPool entryPool, int api, ClassVisitor cv) {
32 super(api, cv); 30 super(api, cv);
33 this.translator = translator; 31 this.translator = translator;
34 this.jarIndex = jarIndex;
35 this.entryPool = entryPool; 32 this.entryPool = entryPool;
36 } 33 }
37 34
38 @Override 35 @Override
39 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 36 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
40 obfSignature = Signature.createSignature(signature); 37 obfClassEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
41 obfClassEntry = new ClassDefEntry(name, obfSignature, new AccessFlags(access)); 38
42 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(obfClassEntry); 39 ClassDefEntry translatedEntry = translator.translate(obfClassEntry);
43 ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName)); 40 String translatedSuper = translatedEntry.getSuperClass() != null ? translatedEntry.getSuperClass().getFullName() : null;
44 String[] translatedInterfaces = new String[interfaces.length]; 41 String[] translatedInterfaces = Arrays.stream(translatedEntry.getInterfaces()).map(ClassEntry::getFullName).toArray(String[]::new);
45 for (int i = 0; i < interfaces.length; i++) { 42
46 translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName(); 43 super.visit(version, translatedEntry.getAccess().getFlags(), translatedEntry.getFullName(), translatedEntry.getSignature().toString(), translatedSuper, translatedInterfaces);
47 }
48 super.visit(version, translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getSignature().toString(), superEntry.getName(), translatedInterfaces);
49 } 44 }
50 45
51 @Override 46 @Override
52 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 47 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
53 FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access)); 48 FieldDefEntry entry = FieldDefEntry.parse(obfClassEntry, access, name, desc, signature);
54 FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry); 49 FieldDefEntry translatedEntry = translator.translate(entry);
55 FieldVisitor fv = super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), value); 50 FieldVisitor fv = super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), value);
56 return new TranslationFieldVisitor(translator, translatedEntry, api, fv); 51 return new TranslationFieldVisitor(translator, translatedEntry, api, fv);
57 } 52 }
58 53
59 @Override 54 @Override
60 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 55 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
61 MethodDefEntry entry = new MethodDefEntry(obfClassEntry, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access)); 56 MethodDefEntry entry = MethodDefEntry.parse(obfClassEntry, access, name, desc, signature);
62 MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry); 57 MethodDefEntry translatedEntry = translator.translate(entry);
63 if (jarIndex.getBridgedMethod(entry) != null) {
64 translatedEntry.getAccess().setBridge();
65 }
66 String[] translatedExceptions = new String[exceptions.length]; 58 String[] translatedExceptions = new String[exceptions.length];
67 for (int i = 0; i < exceptions.length; i++) { 59 for (int i = 0; i < exceptions.length; i++) {
68 translatedExceptions[i] = translator.getTranslatedClass(entryPool.getClass(exceptions[i])).getName(); 60 translatedExceptions[i] = translator.translate(entryPool.getClass(exceptions[i])).getFullName();
69 } 61 }
70 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); 62 MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions);
71 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); 63 return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv);
@@ -73,25 +65,25 @@ public class TranslationClassVisitor extends ClassVisitor {
73 65
74 @Override 66 @Override
75 public void visitInnerClass(String name, String outerName, String innerName, int access) { 67 public void visitInnerClass(String name, String outerName, String innerName, int access) {
76 ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(name, obfSignature, new AccessFlags(access))); 68 ClassDefEntry classEntry = ClassDefEntry.parse(access, name, obfClassEntry.getSignature().toString(), null, new String[0]);
77 String translatedName = translatedEntry.getName(); 69 ClassDefEntry translatedEntry = translator.translate(classEntry);
78 int separatorIndex = translatedName.lastIndexOf("$"); 70 ClassEntry translatedOuterClass = translatedEntry.getOuterClass();
79 String parentName = translatedName.substring(0, separatorIndex); 71 if (translatedOuterClass == null) {
80 String childName = translatedName.substring(separatorIndex + 1); 72 throw new IllegalStateException("Translated inner class did not have outer class");
81 73 }
82 ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(parentName));
83 74
84 // Anonymous classes do not specify an outer or inner name. As we do not translate from the given parameter, ignore if the input is null 75 // Anonymous classes do not specify an outer or inner name. As we do not translate from the given parameter, ignore if the input is null
85 String translatedOuterName = outerName != null ? outerEntry.getName() : null; 76 String translatedName = translatedEntry.getFullName();
86 String translatedInnerName = innerName != null ? childName : null; 77 String translatedOuterName = outerName != null ? translatedOuterClass.getFullName() : null;
78 String translatedInnerName = innerName != null ? translatedEntry.getName() : null;
87 super.visitInnerClass(translatedName, translatedOuterName, translatedInnerName, translatedEntry.getAccess().getFlags()); 79 super.visitInnerClass(translatedName, translatedOuterName, translatedInnerName, translatedEntry.getAccess().getFlags());
88 } 80 }
89 81
90 @Override 82 @Override
91 public void visitOuterClass(String owner, String name, String desc) { 83 public void visitOuterClass(String owner, String name, String desc) {
92 if (desc != null) { 84 if (desc != null) {
93 MethodEntry translatedEntry = translator.getTranslatedMethod(new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc))); 85 MethodEntry translatedEntry = translator.translate(new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)));
94 super.visitOuterClass(translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString()); 86 super.visitOuterClass(translatedEntry.getParent().getFullName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
95 } else { 87 } else {
96 super.visitOuterClass(owner, name, desc); 88 super.visitOuterClass(owner, name, desc);
97 } 89 }
@@ -99,14 +91,14 @@ public class TranslationClassVisitor extends ClassVisitor {
99 91
100 @Override 92 @Override
101 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 93 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
102 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 94 TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc));
103 AnnotationVisitor av = super.visitAnnotation(translatedDesc.toString(), visible); 95 AnnotationVisitor av = super.visitAnnotation(translatedDesc.toString(), visible);
104 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av); 96 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
105 } 97 }
106 98
107 @Override 99 @Override
108 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 100 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
109 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 101 TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc));
110 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible); 102 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, translatedDesc.toString(), visible);
111 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av); 103 return new TranslationAnnotationVisitor(translator, translatedDesc.getTypeEntry(), api, av);
112 } 104 }
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
index e4695fb6..28fc199c 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.mapping.Translator; 3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.mapping.TypeDescriptor; 4import cuchaz.enigma.translation.representation.TypeDescriptor;
5import cuchaz.enigma.mapping.entry.FieldDefEntry; 5import cuchaz.enigma.translation.representation.entry.FieldDefEntry;
6import org.objectweb.asm.AnnotationVisitor; 6import org.objectweb.asm.AnnotationVisitor;
7import org.objectweb.asm.FieldVisitor; 7import org.objectweb.asm.FieldVisitor;
8import org.objectweb.asm.TypePath; 8import org.objectweb.asm.TypePath;
@@ -19,14 +19,14 @@ public class TranslationFieldVisitor extends FieldVisitor {
19 19
20 @Override 20 @Override
21 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 21 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
22 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 22 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
23 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible); 23 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
24 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av); 24 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
25 } 25 }
26 26
27 @Override 27 @Override
28 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 28 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
29 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 29 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
30 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible); 30 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
31 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av); 31 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
32 } 32 }
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
index 6d0d550b..a5a33e69 100644
--- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java
@@ -1,7 +1,11 @@
1package cuchaz.enigma.bytecode.translators; 1package cuchaz.enigma.bytecode.translators;
2 2
3import cuchaz.enigma.mapping.*; 3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.mapping.entry.*; 4import cuchaz.enigma.translation.mapping.NameValidator;
5import cuchaz.enigma.translation.representation.MethodDescriptor;
6import cuchaz.enigma.translation.representation.Signature;
7import cuchaz.enigma.translation.representation.TypeDescriptor;
8import cuchaz.enigma.translation.representation.entry.*;
5import org.objectweb.asm.*; 9import org.objectweb.asm.*;
6 10
7import java.util.Collection; 11import java.util.Collection;
@@ -26,15 +30,15 @@ public class TranslationMethodVisitor extends MethodVisitor {
26 @Override 30 @Override
27 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 31 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
28 FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc)); 32 FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc));
29 FieldEntry translatedEntry = translator.getTranslatedField(entry); 33 FieldEntry translatedEntry = translator.translate(entry);
30 super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString()); 34 super.visitFieldInsn(opcode, translatedEntry.getParent().getFullName(), translatedEntry.getName(), translatedEntry.getDesc().toString());
31 } 35 }
32 36
33 @Override 37 @Override
34 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 38 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
35 MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)); 39 MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc));
36 MethodEntry translatedEntry = translator.getTranslatedMethod(entry); 40 MethodEntry translatedEntry = translator.translate(entry);
37 super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf); 41 super.visitMethodInsn(opcode, translatedEntry.getParent().getFullName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf);
38 } 42 }
39 43
40 @Override 44 @Override
@@ -52,7 +56,7 @@ public class TranslationMethodVisitor extends MethodVisitor {
52 Object object = array[i]; 56 Object object = array[i];
53 if (object instanceof String) { 57 if (object instanceof String) {
54 String type = (String) object; 58 String type = (String) object;
55 array[i] = translator.getTranslatedClass(new ClassEntry(type)).getName(); 59 array[i] = translator.translate(new ClassEntry(type)).getFullName();
56 } 60 }
57 } 61 }
58 return array; 62 return array;
@@ -60,21 +64,21 @@ public class TranslationMethodVisitor extends MethodVisitor {
60 64
61 @Override 65 @Override
62 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 66 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
63 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 67 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
64 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible); 68 AnnotationVisitor av = super.visitAnnotation(typeDesc.toString(), visible);
65 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av); 69 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
66 } 70 }
67 71
68 @Override 72 @Override
69 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 73 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
70 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 74 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
71 AnnotationVisitor av = super.visitParameterAnnotation(parameter, typeDesc.toString(), visible); 75 AnnotationVisitor av = super.visitParameterAnnotation(parameter, typeDesc.toString(), visible);
72 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av); 76 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
73 } 77 }
74 78
75 @Override 79 @Override
76 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { 80 public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
77 TypeDescriptor typeDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 81 TypeDescriptor typeDesc = translator.translate(new TypeDescriptor(desc));
78 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, typeDesc.toString(), visible); 82 AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, typeDesc.toString(), visible);
79 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av); 83 return new TranslationAnnotationVisitor(translator, typeDesc.getTypeEntry(), api, av);
80 } 84 }
@@ -83,19 +87,18 @@ public class TranslationMethodVisitor extends MethodVisitor {
83 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 87 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
84 hasParameterMeta = true; 88 hasParameterMeta = true;
85 89
86 String translatedSignature = translator.getTranslatedSignature(Signature.createTypedSignature(signature)).toString(); 90 String translatedSignature = translator.translate(Signature.createTypedSignature(signature)).toString();
87 int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index); 91 int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index);
88 92
89 if (argumentIndex >= 0) { 93 if (argumentIndex >= 0) {
90 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, new TypeDescriptor(desc)); 94 LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, true, new TypeDescriptor(desc));
91 LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry); 95 LocalVariableDefEntry translatedEntry = translator.translate(entry);
92 String translatedName = translatedEntry.getName(); 96 String translatedName = translatedEntry.getName();
93 97
94 // TODO: Better name inference
95 if (translatedName.equals(entry.getName())) { 98 if (translatedName.equals(entry.getName())) {
96 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); 99 List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs();
97 List<TypeDescriptor> translatedArguments = arguments.stream() 100 List<TypeDescriptor> translatedArguments = arguments.stream()
98 .map(translator::getTranslatedTypeDesc) 101 .map(translator::translate)
99 .collect(Collectors.toList()); 102 .collect(Collectors.toList());
100 103
101 boolean argument = argumentIndex < arguments.size(); 104 boolean argument = argumentIndex < arguments.size();
@@ -109,42 +112,42 @@ public class TranslationMethodVisitor extends MethodVisitor {
109 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index); 112 super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index);
110 } else { 113 } else {
111 // Handle "this" variable 114 // Handle "this" variable
112 TypeDescriptor translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)); 115 TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc));
113 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index); 116 super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index);
114 } 117 }
115 } 118 }
116 119
117 @Override 120 @Override
118 public void visitTypeInsn(int opcode, String type) { 121 public void visitTypeInsn(int opcode, String type) {
119 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type)); 122 ClassEntry translatedEntry = translator.translate(new ClassEntry(type));
120 super.visitTypeInsn(opcode, translatedEntry.getName()); 123 super.visitTypeInsn(opcode, translatedEntry.getFullName());
121 } 124 }
122 125
123 @Override 126 @Override
124 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 127 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
125 MethodDescriptor translatedMethodDesc = translator.getTranslatedMethodDesc(new MethodDescriptor(desc)); 128 MethodDescriptor translatedMethodDesc = translator.translate(new MethodDescriptor(desc));
126 Object[] translatedBsmArgs = new Object[bsmArgs.length]; 129 Object[] translatedBsmArgs = new Object[bsmArgs.length];
127 for (int i = 0; i < bsmArgs.length; i++) { 130 for (int i = 0; i < bsmArgs.length; i++) {
128 translatedBsmArgs[i] = translator.getTranslatedValue(bsmArgs[i]); 131 translatedBsmArgs[i] = AsmObjectTranslator.translateValue(translator, bsmArgs[i]);
129 } 132 }
130 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), translator.getTranslatedHandle(bsm), translatedBsmArgs); 133 super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), AsmObjectTranslator.translateHandle(translator, bsm), translatedBsmArgs);
131 } 134 }
132 135
133 @Override 136 @Override
134 public void visitLdcInsn(Object cst) { 137 public void visitLdcInsn(Object cst) {
135 super.visitLdcInsn(translator.getTranslatedValue(cst)); 138 super.visitLdcInsn(AsmObjectTranslator.translateValue(translator, cst));
136 } 139 }
137 140
138 @Override 141 @Override
139 public void visitMultiANewArrayInsn(String desc, int dims) { 142 public void visitMultiANewArrayInsn(String desc, int dims) {
140 super.visitMultiANewArrayInsn(translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString(), dims); 143 super.visitMultiANewArrayInsn(translator.translate(new TypeDescriptor(desc)).toString(), dims);
141 } 144 }
142 145
143 @Override 146 @Override
144 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 147 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
145 if (type != null) { 148 if (type != null) {
146 ClassEntry translatedEntry = translator.getTranslatedClass(new ClassEntry(type)); 149 ClassEntry translatedEntry = translator.translate(new ClassEntry(type));
147 super.visitTryCatchBlock(start, end, handler, translatedEntry.getName()); 150 super.visitTryCatchBlock(start, end, handler, translatedEntry.getFullName());
148 } else { 151 } else {
149 super.visitTryCatchBlock(start, end, handler, type); 152 super.visitTryCatchBlock(start, end, handler, type);
150 } 153 }
@@ -159,7 +162,7 @@ public class TranslationMethodVisitor extends MethodVisitor {
159 162
160 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { 163 for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) {
161 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, offset, "", true); 164 LocalVariableEntry entry = new LocalVariableEntry(methodEntry, offset, "", true);
162 LocalVariableEntry translatedEntry = translator.getTranslatedVariable(entry); 165 LocalVariableEntry translatedEntry = translator.translate(entry);
163 String translatedName = translatedEntry.getName(); 166 String translatedName = translatedEntry.getName();
164 if (translatedName.equals(entry.getName())) { 167 if (translatedName.equals(entry.getName())) {
165 super.visitParameter(inferArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0); 168 super.visitParameter(inferArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0);
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index 8863386d..c3b7288c 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -17,14 +17,13 @@ import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
18import cuchaz.enigma.gui.node.ClassSelectorClassNode; 18import cuchaz.enigma.gui.node.ClassSelectorClassNode;
19import cuchaz.enigma.gui.node.ClassSelectorPackageNode; 19import cuchaz.enigma.gui.node.ClassSelectorPackageNode;
20import cuchaz.enigma.mapping.entry.ClassEntry; 20import cuchaz.enigma.translation.representation.entry.ClassEntry;
21import cuchaz.enigma.throwables.IllegalNameException; 21import cuchaz.enigma.throwables.IllegalNameException;
22 22
23import javax.swing.*; 23import javax.swing.*;
24import javax.swing.event.CellEditorListener; 24import javax.swing.event.CellEditorListener;
25import javax.swing.event.ChangeEvent; 25import javax.swing.event.ChangeEvent;
26import javax.swing.tree.*; 26import javax.swing.tree.*;
27import java.awt.*;
28import java.awt.event.MouseAdapter; 27import java.awt.event.MouseAdapter;
29import java.awt.event.MouseEvent; 28import java.awt.event.MouseEvent;
30import java.util.*; 29import java.util.*;
@@ -32,7 +31,7 @@ import java.util.List;
32 31
33public class ClassSelector extends JTree { 32public class ClassSelector extends JTree {
34 33
35 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); 34 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName);
36 private DefaultMutableTreeNode rootNodes; 35 private DefaultMutableTreeNode rootNodes;
37 private ClassSelectionListener selectionListener; 36 private ClassSelectionListener selectionListener;
38 private RenameSelectionListener renameSelectionListener; 37 private RenameSelectionListener renameSelectionListener;
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
index 137c7303..08100438 100644
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java
@@ -16,9 +16,8 @@ import cuchaz.enigma.Deobfuscator;
16import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
17import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.analysis.Token; 18import cuchaz.enigma.analysis.Token;
19import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; 19import cuchaz.enigma.translation.representation.entry.ClassEntry;
20import cuchaz.enigma.mapping.entry.ClassEntry; 20import cuchaz.enigma.translation.representation.entry.Entry;
21import cuchaz.enigma.mapping.entry.Entry;
22import de.sciss.syntaxpane.DefaultSyntaxKit; 21import de.sciss.syntaxpane.DefaultSyntaxKit;
23 22
24import javax.swing.*; 23import javax.swing.*;
@@ -33,7 +32,6 @@ public class CodeReader extends JEditorPane {
33 private static final long serialVersionUID = 3673180950485748810L; 32 private static final long serialVersionUID = 3673180950485748810L;
34 33
35 private static final Object lock = new Object(); 34 private static final Object lock = new Object();
36 private SelectionHighlightPainter selectionHighlightPainter;
37 private SourceIndex sourceIndex; 35 private SourceIndex sourceIndex;
38 private SelectionListener selectionListener; 36 private SelectionListener selectionListener;
39 37
@@ -58,8 +56,6 @@ public class CodeReader extends JEditorPane {
58 } 56 }
59 } 57 }
60 }); 58 });
61
62 selectionHighlightPainter = new SelectionHighlightPainter();
63 } 59 }
64 60
65 // HACKHACK: someday we can update the main GUI to use this code reader 61 // HACKHACK: someday we can update the main GUI to use this code reader
@@ -144,7 +140,7 @@ public class CodeReader extends JEditorPane {
144 140
145 // decompile it 141 // decompile it
146 142
147 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); 143 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getName());
148 String source = deobfuscator.getSource(sourceTree); 144 String source = deobfuscator.getSource(sourceTree);
149 setCode(source); 145 setCode(source);
150 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); 146 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
@@ -155,52 +151,7 @@ public class CodeReader extends JEditorPane {
155 }).start(); 151 }).start();
156 } 152 }
157 153
158 public void navigateToClassDeclaration(ClassEntry classEntry) {
159
160 // navigate to the class declaration
161 Token token = sourceIndex.getDeclarationToken(classEntry);
162 if (token == null) {
163 // couldn't find the class declaration token, might be an anonymous class
164 // look for any declaration in that class instead
165 for (Entry entry : sourceIndex.declarations()) {
166 if (entry.getOwnerClassEntry().equals(classEntry)) {
167 token = sourceIndex.getDeclarationToken(entry);
168 break;
169 }
170 }
171 }
172
173 if (token != null) {
174 navigateToToken(token);
175 } else {
176 // couldn't find anything =(
177 System.out.println("Unable to find declaration in source for " + classEntry);
178 }
179 }
180
181 public void navigateToToken(final Token token) {
182 navigateToToken(this, token, selectionHighlightPainter);
183 }
184
185 public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) {
186 for (Token token : tokens) {
187 setHighlightedToken(token, painter);
188 }
189 }
190
191 public void setHighlightedToken(Token token, HighlightPainter painter) {
192 try {
193 getHighlighter().addHighlight(token.start, token.end, painter);
194 } catch (BadLocationException ex) {
195 throw new IllegalArgumentException(ex);
196 }
197 }
198
199 public void clearHighlights() {
200 getHighlighter().removeAllHighlights();
201 }
202
203 public interface SelectionListener { 154 public interface SelectionListener {
204 void onSelect(EntryReference<Entry, Entry> reference); 155 void onSelect(EntryReference<Entry<?>, Entry<?>> reference);
205 } 156 }
206} 157}
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 53500aa4..d119735c 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -29,9 +29,9 @@ import 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;
31import cuchaz.enigma.gui.panels.PanelObf; 31import cuchaz.enigma.gui.panels.PanelObf;
32import cuchaz.enigma.mapping.*;
33import cuchaz.enigma.mapping.entry.*;
34import cuchaz.enigma.throwables.IllegalNameException; 32import cuchaz.enigma.throwables.IllegalNameException;
33import cuchaz.enigma.translation.mapping.AccessModifier;
34import cuchaz.enigma.translation.representation.entry.*;
35import cuchaz.enigma.utils.Utils; 35import cuchaz.enigma.utils.Utils;
36import de.sciss.syntaxpane.DefaultSyntaxKit; 36import de.sciss.syntaxpane.DefaultSyntaxKit;
37 37
@@ -44,8 +44,8 @@ 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.File;
48import java.io.IOException; 47import java.io.IOException;
48import java.nio.file.Path;
49import java.util.*; 49import java.util.*;
50import java.util.List; 50import java.util.List;
51import java.util.function.Function; 51import java.util.function.Function;
@@ -58,7 +58,7 @@ public class Gui {
58 58
59 private final MenuBar menuBar; 59 private final MenuBar menuBar;
60 // state 60 // state
61 public EntryReference<Entry, Entry> reference; 61 public EntryReference<Entry<?>, Entry<?>> reference;
62 public FileDialog jarFileChooser; 62 public FileDialog jarFileChooser;
63 public FileDialog tinyMappingsFileChooser; 63 public FileDialog tinyMappingsFileChooser;
64 public JFileChooser enigmaMappingsFileChooser; 64 public JFileChooser enigmaMappingsFileChooser;
@@ -222,7 +222,7 @@ public class Gui {
222 222
223 Object node = path.getLastPathComponent(); 223 Object node = path.getLastPathComponent();
224 if (node instanceof ReferenceTreeNode) { 224 if (node instanceof ReferenceTreeNode) {
225 ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); 225 ReferenceTreeNode<Entry<?>, Entry<?>> referenceNode = ((ReferenceTreeNode<Entry<?>, Entry<?>>) node);
226 if (referenceNode.getReference() != null) { 226 if (referenceNode.getReference() != null) {
227 navigateTo(referenceNode.getReference()); 227 navigateTo(referenceNode.getReference());
228 } else { 228 } else {
@@ -250,10 +250,10 @@ public class Gui {
250 tokens.setPreferredSize(new Dimension(0, 200)); 250 tokens.setPreferredSize(new Dimension(0, 200));
251 tokens.setMinimumSize(new Dimension(0, 200)); 251 tokens.setMinimumSize(new Dimension(0, 200));
252 JSplitPane callPanel = new JSplitPane( 252 JSplitPane callPanel = new JSplitPane(
253 JSplitPane.VERTICAL_SPLIT, 253 JSplitPane.VERTICAL_SPLIT,
254 true, 254 true,
255 new JScrollPane(callsTree), 255 new JScrollPane(callsTree),
256 new JScrollPane(tokens) 256 new JScrollPane(tokens)
257 ); 257 );
258 callPanel.setResizeWeight(1); // let the top side take all the slack 258 callPanel.setResizeWeight(1); // let the top side take all the slack
259 callPanel.resetToPreferredSizes(); 259 callPanel.resetToPreferredSizes();
@@ -368,9 +368,9 @@ public class Gui {
368 this.deobfPanel.deobfClasses.setClasses(deobfClasses); 368 this.deobfPanel.deobfClasses.setClasses(deobfClasses);
369 } 369 }
370 370
371 public void setMappingsFile(File file) { 371 public void setMappingsFile(Path path) {
372 this.enigmaMappingsFileChooser.setSelectedFile(file); 372 this.enigmaMappingsFileChooser.setSelectedFile(path != null ? path.toFile() : null);
373 this.menuBar.saveMappingsMenu.setEnabled(file != null); 373 this.menuBar.saveMappingsMenu.setEnabled(path != null);
374 } 374 }
375 375
376 public void setSource(String source) { 376 public void setSource(String source) {
@@ -427,7 +427,7 @@ public class Gui {
427 } 427 }
428 } 428 }
429 429
430 private void showReference(EntryReference<Entry, Entry> reference) { 430 private void showReference(EntryReference<Entry<?>, Entry<?>> reference) {
431 if (reference == null) { 431 if (reference == null) {
432 infoPanel.clearReference(); 432 infoPanel.clearReference();
433 return; 433 return;
@@ -453,29 +453,29 @@ public class Gui {
453 453
454 private void showLocalVariableEntry(LocalVariableEntry entry) { 454 private void showLocalVariableEntry(LocalVariableEntry entry) {
455 addNameValue(infoPanel, "Variable", entry.getName()); 455 addNameValue(infoPanel, "Variable", entry.getName());
456 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName()); 456 addNameValue(infoPanel, "Class", entry.getContainingClass().getFullName());
457 addNameValue(infoPanel, "Method", entry.getOwnerEntry().getName()); 457 addNameValue(infoPanel, "Method", entry.getParent().getName());
458 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); 458 addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex()));
459 } 459 }
460 460
461 private void showClassEntry(ClassEntry entry) { 461 private void showClassEntry(ClassEntry entry) {
462 addNameValue(infoPanel, "Class", entry.getName()); 462 addNameValue(infoPanel, "Class", entry.getFullName());
463 addModifierComboBox(infoPanel, "Modifier", entry); 463 addModifierComboBox(infoPanel, "Modifier", entry);
464 } 464 }
465 465
466 private void showFieldEntry(FieldEntry entry) { 466 private void showFieldEntry(FieldEntry entry) {
467 addNameValue(infoPanel, "Field", entry.getName()); 467 addNameValue(infoPanel, "Field", entry.getName());
468 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName()); 468 addNameValue(infoPanel, "Class", entry.getParent().getFullName());
469 addNameValue(infoPanel, "TypeDescriptor", entry.getDesc().toString()); 469 addNameValue(infoPanel, "TypeDescriptor", entry.getDesc().toString());
470 addModifierComboBox(infoPanel, "Modifier", entry); 470 addModifierComboBox(infoPanel, "Modifier", entry);
471 } 471 }
472 472
473 private void showMethodEntry(MethodEntry entry) { 473 private void showMethodEntry(MethodEntry entry) {
474 if (entry.isConstructor()) { 474 if (entry.isConstructor()) {
475 addNameValue(infoPanel, "Constructor", entry.getOwnerClassEntry().getName()); 475 addNameValue(infoPanel, "Constructor", entry.getParent().getFullName());
476 } else { 476 } else {
477 addNameValue(infoPanel, "Method", entry.getName()); 477 addNameValue(infoPanel, "Method", entry.getName());
478 addNameValue(infoPanel, "Class", entry.getOwnerClassEntry().getName()); 478 addNameValue(infoPanel, "Class", entry.getParent().getFullName());
479 } 479 }
480 addNameValue(infoPanel, "MethodDescriptor", entry.getDesc().toString()); 480 addNameValue(infoPanel, "MethodDescriptor", entry.getDesc().toString());
481 addModifierComboBox(infoPanel, "Modifier", entry); 481 addModifierComboBox(infoPanel, "Modifier", entry);
@@ -494,7 +494,7 @@ public class Gui {
494 container.add(panel); 494 container.add(panel);
495 } 495 }
496 496
497 private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) { 497 private JComboBox<AccessModifier> addModifierComboBox(JPanel container, String name, Entry entry) {
498 if (!getController().entryIsInJar(entry)) 498 if (!getController().entryIsInJar(entry))
499 return null; 499 return null;
500 JPanel panel = new JPanel(); 500 JPanel panel = new JPanel();
@@ -502,7 +502,7 @@ public class Gui {
502 JLabel label = new JLabel(name + ":", JLabel.RIGHT); 502 JLabel label = new JLabel(name + ":", JLabel.RIGHT);
503 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 503 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
504 panel.add(label); 504 panel.add(label);
505 JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); 505 JComboBox<AccessModifier> combo = new JComboBox<>(AccessModifier.values());
506 ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); 506 ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT);
507 combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 507 combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
508 combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); 508 combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal());
@@ -520,11 +520,13 @@ public class Gui {
520 boolean isToken = token != null; 520 boolean isToken = token != null;
521 521
522 reference = this.controller.getDeobfReference(token); 522 reference = this.controller.getDeobfReference(token);
523 boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; 523
524 boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; 524 Entry<?> referenceEntry = reference != null ? reference.entry : null;
525 boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry && !((MethodEntry) reference.entry).isConstructor(); 525 boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry;
526 boolean isConstructorEntry = isToken && reference.entry instanceof MethodEntry && ((MethodEntry) reference.entry).isConstructor(); 526 boolean isFieldEntry = isToken && referenceEntry instanceof FieldEntry;
527 boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); 527 boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor();
528 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor();
529 boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry);
528 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); 530 boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference);
529 531
530 if (isToken) { 532 if (isToken) {
@@ -542,14 +544,14 @@ public class Gui {
542 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 544 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation());
543 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); 545 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable);
544 546
545 if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { 547 if (isToken && this.controller.entryHasDeobfuscatedName(referenceEntry)) {
546 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); 548 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated");
547 } else { 549 } else {
548 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); 550 this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated");
549 } 551 }
550 } 552 }
551 553
552 public void navigateTo(Entry entry) { 554 public void navigateTo(Entry<?> entry) {
553 if (!this.controller.entryIsInJar(entry)) { 555 if (!this.controller.entryIsInJar(entry)) {
554 // entry is not in the jar. Ignore it 556 // entry is not in the jar. Ignore it
555 return; 557 return;
@@ -560,7 +562,7 @@ public class Gui {
560 this.controller.openDeclaration(entry); 562 this.controller.openDeclaration(entry);
561 } 563 }
562 564
563 private void navigateTo(EntryReference<Entry, Entry> reference) { 565 private void navigateTo(EntryReference<Entry<?>, Entry<?>> reference) {
564 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { 566 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) {
565 return; 567 return;
566 } 568 }
@@ -613,7 +615,7 @@ public class Gui {
613 String newName = text.getText(); 615 String newName = text.getText();
614 if (saveName && newName != null && !newName.isEmpty()) { 616 if (saveName && newName != null && !newName.isEmpty()) {
615 try { 617 try {
616 this.controller.rename(reference, newName); 618 this.controller.rename(reference, newName, true);
617 } catch (IllegalNameException ex) { 619 } catch (IllegalNameException ex) {
618 text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 620 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
619 text.setToolTipText(ex.getReason()); 621 text.setToolTipText(ex.getReason());
@@ -737,13 +739,13 @@ public class Gui {
737 739
738 public void showDiscardDiag(Function<Integer, Void> callback, String... options) { 740 public void showDiscardDiag(Function<Integer, Void> callback, String... options) {
739 int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, 741 int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION,
740 JOptionPane.QUESTION_MESSAGE, null, options, options[2]); 742 JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
741 callback.apply(response); 743 callback.apply(response);
742 } 744 }
743 745
744 public void saveMapping() throws IOException { 746 public void saveMapping() throws IOException {
745 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) 747 if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION)
746 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); 748 this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath());
747 } 749 }
748 750
749 public void close() { 751 public void close() {
@@ -782,7 +784,7 @@ public class Gui {
782 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); 784 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
783 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 785 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
784 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 786 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
785 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); 787 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getFullName()), dataChild.getFullName(), false);
786 childNode.setUserObject(dataChild); 788 childNode.setUserObject(dataChild);
787 } 789 }
788 node.setUserObject(data); 790 node.setUserObject(data);
@@ -791,19 +793,19 @@ public class Gui {
791 } 793 }
792 // class rename 794 // class rename
793 else if (data instanceof ClassEntry) 795 else if (data instanceof ClassEntry)
794 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); 796 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false);
795 } 797 }
796 798
797 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) { 799 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName) {
798 String oldEntry = deobfReference.entry.getOwnerClassEntry().getPackageName(); 800 String oldEntry = deobfReference.entry.getContainingClass().getPackageName();
799 String newEntry = new ClassEntry(newName).getPackageName(); 801 String newEntry = new ClassEntry(newName).getPackageName();
800 moveClassTree(deobfReference, newName, oldEntry == null, 802 moveClassTree(deobfReference, newName, oldEntry == null,
801 newEntry == null); 803 newEntry == null);
802 } 804 }
803 805
804 // TODO: getExpansionState will *not* actually update itself based on name changes! 806 // TODO: getExpansionState will *not* actually update itself based on name changes!
805 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { 807 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean isOldOb, boolean isNewOb) {
806 ClassEntry oldEntry = deobfReference.entry.getOwnerClassEntry(); 808 ClassEntry oldEntry = deobfReference.entry.getContainingClass();
807 ClassEntry newEntry = new ClassEntry(newName); 809 ClassEntry newEntry = new ClassEntry(newName);
808 810
809 // Ob -> deob 811 // Ob -> deob
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 69aefe58..06cb33e3 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -20,19 +20,25 @@ import cuchaz.enigma.analysis.*;
20import cuchaz.enigma.api.EnigmaPlugin; 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.mapping.*;
24import cuchaz.enigma.mapping.entry.ClassEntry;
25import cuchaz.enigma.mapping.entry.Entry;
26import cuchaz.enigma.mapping.entry.FieldEntry;
27import cuchaz.enigma.mapping.entry.MethodEntry;
28import cuchaz.enigma.throwables.MappingParseException; 23import cuchaz.enigma.throwables.MappingParseException;
24import cuchaz.enigma.translation.Translator;
25import cuchaz.enigma.translation.mapping.*;
26import cuchaz.enigma.translation.mapping.serde.MappingFormat;
27import cuchaz.enigma.translation.mapping.tree.EntryTree;
28import cuchaz.enigma.translation.representation.entry.ClassEntry;
29import cuchaz.enigma.translation.representation.entry.Entry;
30import cuchaz.enigma.translation.representation.entry.FieldEntry;
31import cuchaz.enigma.translation.representation.entry.MethodEntry;
29import cuchaz.enigma.utils.ReadableToken; 32import cuchaz.enigma.utils.ReadableToken;
30 33
34import javax.annotation.Nullable;
31import java.awt.event.ItemEvent; 35import java.awt.event.ItemEvent;
32import java.io.File; 36import java.io.File;
33import java.io.IOException; 37import java.io.IOException;
38import java.nio.file.Path;
34import java.util.*; 39import java.util.*;
35import java.util.jar.JarFile; 40import java.util.jar.JarFile;
41import java.util.stream.Collectors;
36 42
37public class GuiController { 43public class GuiController {
38 44
@@ -40,27 +46,26 @@ public class GuiController {
40 private Gui gui; 46 private Gui gui;
41 private SourceIndex index; 47 private SourceIndex index;
42 private ClassEntry currentObfClass; 48 private ClassEntry currentObfClass;
43 private boolean isDirty; 49 private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack;
44 private Deque<EntryReference<Entry, Entry>> referenceStack; 50
51 private Path loadedMappingPath;
52 private MappingFormat loadedMappingFormat;
45 53
46 public GuiController(Gui gui) { 54 public GuiController(Gui gui) {
47 this.gui = gui; 55 this.gui = gui;
48 this.deobfuscator = null; 56 this.deobfuscator = null;
49 this.index = null; 57 this.index = null;
50 this.currentObfClass = null; 58 this.currentObfClass = null;
51 this.isDirty = false;
52 this.referenceStack = Queues.newArrayDeque(); 59 this.referenceStack = Queues.newArrayDeque();
53 } 60 }
54 61
55 public boolean isDirty() { 62 public boolean isDirty() {
56 return this.isDirty; 63 return deobfuscator.getMapper().isDirty();
57 } 64 }
58 65
59 public void openJar(final JarFile jar) throws IOException { 66 public void openJar(final JarFile jar) throws IOException {
60 this.gui.onStartOpenJar("Loading JAR..."); 67 this.gui.onStartOpenJar("Loading JAR...");
61 this.deobfuscator = new Deobfuscator(jar, (msg) -> { 68 this.deobfuscator = new Deobfuscator(jar, this.gui::onStartOpenJar);
62 this.gui.onStartOpenJar(msg);
63 });
64 this.gui.onFinishOpenJar(jar.getName()); 69 this.gui.onFinishOpenJar(jar.getName());
65 refreshClasses(); 70 refreshClasses();
66 } 71 }
@@ -70,43 +75,37 @@ public class GuiController {
70 this.gui.onCloseJar(); 75 this.gui.onCloseJar();
71 } 76 }
72 77
73 public void openEnigmaMappings(File file) throws IOException, MappingParseException { 78 public void openMappings(MappingFormat format, Path path) throws IOException, MappingParseException {
74 this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); 79 EntryTree<EntryMapping> mappings = format.read(path);
75 this.isDirty = false; 80 deobfuscator.setMappings(mappings);
76 this.gui.setMappingsFile(file); 81
82 gui.setMappingsFile(path);
83 loadedMappingFormat = format;
84
77 refreshClasses(); 85 refreshClasses();
78 refreshCurrentClass(); 86 refreshCurrentClass();
79 } 87 }
80 88
81 public void openTinyMappings(File file) throws IOException, MappingParseException { 89 public void saveMappings(Path path) {
82 this.deobfuscator.setMappings(new MappingsTinyReader().read(file)); 90 saveMappings(loadedMappingFormat, path);
83 this.isDirty = false;
84 this.gui.setMappingsFile(file);
85 refreshClasses();
86 refreshCurrentClass();
87 } 91 }
88 92
89 public void saveMappings(File file) throws IOException { 93 public void saveMappings(MappingFormat format, Path path) {
90 Mappings mappings = this.deobfuscator.getMappings(); 94 EntryRemapper mapper = deobfuscator.getMapper();
91 switch (mappings.getOriginMappingFormat()) {
92 case SRG_FILE:
93 saveSRGMappings(file);
94 break;
95 default:
96 saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat());
97 break;
98 }
99 95
100 } 96 MappingDelta delta = mapper.takeMappingDelta();
97 boolean saveAll = !path.equals(loadedMappingPath);
101 98
102 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { 99 ProgressDialog.runInThread(this.gui.getFrame(), progress -> {
103 this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); 100 if (saveAll) {
104 this.isDirty = false; 101 format.write(mapper.getObfToDeobf(), path, progress);
105 } 102 } else {
103 format.write(mapper.getObfToDeobf(), delta, path, progress);
104 }
105 });
106 106
107 public void saveSRGMappings(File file) throws IOException { 107 loadedMappingFormat = format;
108 this.deobfuscator.getMappings().saveSRGMappings(file); 108 loadedMappingPath = path;
109 this.isDirty = false;
110 } 109 }
111 110
112 public void closeMappings() { 111 public void closeMappings() {
@@ -116,11 +115,6 @@ public class GuiController {
116 refreshCurrentClass(); 115 refreshCurrentClass();
117 } 116 }
118 117
119 public void rebuildMethodNames() {
120 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress));
121 this.isDirty = true;
122 }
123
124 public void exportSource(final File dirOut) { 118 public void exportSource(final File dirOut) {
125 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); 119 ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress));
126 } 120 }
@@ -136,7 +130,8 @@ public class GuiController {
136 return this.index.getReferenceToken(pos); 130 return this.index.getReferenceToken(pos);
137 } 131 }
138 132
139 public EntryReference<Entry, Entry> getDeobfReference(Token token) { 133 @Nullable
134 public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) {
140 if (this.index == null) { 135 if (this.index == null) {
141 return null; 136 return null;
142 } 137 }
@@ -148,44 +143,52 @@ public class GuiController {
148 return null; 143 return null;
149 } 144 }
150 return new ReadableToken( 145 return new ReadableToken(
151 this.index.getLineNumber(token.start), 146 this.index.getLineNumber(token.start),
152 this.index.getColumnNumber(token.start), 147 this.index.getColumnNumber(token.start),
153 this.index.getColumnNumber(token.end) 148 this.index.getColumnNumber(token.end)
154 ); 149 );
155 } 150 }
156 151
157 public boolean entryHasDeobfuscatedName(Entry deobfEntry) { 152 public boolean entryHasDeobfuscatedName(Entry<?> deobfEntry) {
158 return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); 153 EntryResolver resolver = this.deobfuscator.getMapper().getDeobfResolver();
154 Entry<?> resolvedEntry = resolver.resolveFirstEntry(deobfEntry, ResolutionStrategy.RESOLVE_ROOT);
155 return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.getMapper().obfuscate(resolvedEntry));
159 } 156 }
160 157
161 public boolean entryIsInJar(Entry deobfEntry) { 158 public boolean entryIsInJar(Entry<?> deobfEntry) {
162 return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); 159 if (deobfEntry == null) return false;
160 return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.getMapper().obfuscate(deobfEntry));
163 } 161 }
164 162
165 public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { 163 public boolean referenceIsRenameable(EntryReference<Entry<?>, Entry<?>> deobfReference) {
166 return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference)); 164 if (deobfReference == null) return false;
165 return this.deobfuscator.isRenameable(this.deobfuscator.getMapper().obfuscate(deobfReference));
167 } 166 }
168 167
169 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { 168 public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) {
170 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 169 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
171 ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry); 170 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
171 ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, obfClassEntry);
172 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); 172 return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry);
173 } 173 }
174 174
175 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { 175 public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) {
176 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 176 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
177 return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry); 177 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
178 return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, obfClassEntry);
178 } 179 }
179 180
180 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { 181 public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) {
181 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 182 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
182 MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry); 183 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
184 MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, obfMethodEntry);
183 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); 185 return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry);
184 } 186 }
185 187
186 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { 188 public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) {
187 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 189 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
188 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry); 190 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
191 List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, obfMethodEntry);
189 if (rootNodes.isEmpty()) { 192 if (rootNodes.isEmpty()) {
190 return null; 193 return null;
191 } 194 }
@@ -196,34 +199,32 @@ public class GuiController {
196 } 199 }
197 200
198 public ClassReferenceTreeNode getClassReferences(ClassEntry deobfClassEntry) { 201 public ClassReferenceTreeNode getClassReferences(ClassEntry deobfClassEntry) {
199 ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); 202 ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry);
200 ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfClassEntry); 203 Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator();
204 ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, obfClassEntry);
201 rootNode.load(this.deobfuscator.getJarIndex(), true); 205 rootNode.load(this.deobfuscator.getJarIndex(), true);
202 return rootNode; 206 return rootNode;
203 } 207 }
204 208
205 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { 209 public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) {
206 FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); 210 FieldEntry obfFieldEntry = this.deobfuscator.getMapper().obfuscate(deobfFieldEntry);
207 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfFieldEntry); 211 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
212 FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, obfFieldEntry);
208 rootNode.load(this.deobfuscator.getJarIndex(), true); 213 rootNode.load(this.deobfuscator.getJarIndex(), true);
209 return rootNode; 214 return rootNode;
210 } 215 }
211 216
212 public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry, boolean recursive) { 217 public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry, boolean recursive) {
213 MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); 218 MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry);
214 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.DEOBFUSCATING), obfMethodEntry); 219 Translator translator = this.deobfuscator.getMapper().getDeobfuscator();
220 MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, obfMethodEntry);
215 rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); 221 rootNode.load(this.deobfuscator.getJarIndex(), true, recursive);
216 return rootNode; 222 return rootNode;
217 } 223 }
218 224
219 public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { 225 public void rename(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean refreshClassTree) {
220 rename(deobfReference, newName, true, true); 226 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference);
221 } 227 this.deobfuscator.rename(obfReference.getNameableEntry(), newName);
222
223 public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) {
224 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
225 this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache);
226 this.isDirty = true;
227 228
228 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) 229 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
229 this.gui.moveClassTree(deobfReference, newName); 230 this.gui.moveClassTree(deobfReference, newName);
@@ -231,39 +232,37 @@ public class GuiController {
231 232
232 } 233 }
233 234
234 public void removeMapping(EntryReference<Entry, Entry> deobfReference) { 235 public void removeMapping(EntryReference<Entry<?>, Entry<?>> deobfReference) {
235 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 236 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference);
236 this.deobfuscator.removeMapping(obfReference.getNameableEntry()); 237 this.deobfuscator.removeMapping(obfReference.getNameableEntry());
237 this.isDirty = true;
238 if (deobfReference.entry instanceof ClassEntry) 238 if (deobfReference.entry instanceof ClassEntry)
239 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); 239 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true);
240 refreshCurrentClass(obfReference); 240 refreshCurrentClass(obfReference);
241 } 241 }
242 242
243 public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { 243 public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> deobfReference) {
244 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 244 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference);
245 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); 245 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry());
246 this.isDirty = true;
247 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) 246 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
248 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); 247 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false);
249 refreshCurrentClass(obfReference); 248 refreshCurrentClass(obfReference);
250 } 249 }
251 250
252 public void openDeclaration(Entry deobfEntry) { 251 public void openDeclaration(Entry<?> deobfEntry) {
253 if (deobfEntry == null) { 252 if (deobfEntry == null) {
254 throw new IllegalArgumentException("Entry cannot be null!"); 253 throw new IllegalArgumentException("Entry cannot be null!");
255 } 254 }
256 openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); 255 openReference(new EntryReference<>(deobfEntry, deobfEntry.getName()));
257 } 256 }
258 257
259 public void openReference(EntryReference<Entry, Entry> deobfReference) { 258 public void openReference(EntryReference<Entry<?>, Entry<?>> deobfReference) {
260 if (deobfReference == null) { 259 if (deobfReference == null) {
261 throw new IllegalArgumentException("Reference cannot be null!"); 260 throw new IllegalArgumentException("Reference cannot be null!");
262 } 261 }
263 262
264 // get the reference target class 263 // get the reference target class
265 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 264 EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference);
266 ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); 265 ClassEntry obfClassEntry = obfReference.getLocationClassEntry();
267 if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { 266 if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) {
268 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); 267 throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!");
269 } 268 }
@@ -276,24 +275,30 @@ public class GuiController {
276 } 275 }
277 } 276 }
278 277
279 private void showReference(EntryReference<Entry, Entry> obfReference) { 278 private void showReference(EntryReference<Entry<?>, Entry<?>> obfReference) {
280 EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); 279 EntryRemapper mapper = this.deobfuscator.getMapper();
281 Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); 280
281 Collection<Token> tokens = mapper.getObfResolver().resolveReference(obfReference, ResolutionStrategy.RESOLVE_ROOT)
282 .stream()
283 .map(mapper::deobfuscate)
284 .flatMap(reference -> index.getReferenceTokens(reference).stream())
285 .collect(Collectors.toList());
286
282 if (tokens.isEmpty()) { 287 if (tokens.isEmpty()) {
283 // DEBUG 288 // DEBUG
284 System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); 289 System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentObfClass));
285 } else { 290 } else {
286 this.gui.showTokens(tokens); 291 this.gui.showTokens(tokens);
287 } 292 }
288 } 293 }
289 294
290 public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { 295 public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> deobfReference) {
291 this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); 296 this.referenceStack.push(this.deobfuscator.getMapper().obfuscate(deobfReference));
292 } 297 }
293 298
294 public void openPreviousReference() { 299 public void openPreviousReference() {
295 if (hasPreviousLocation()) { 300 if (hasPreviousLocation()) {
296 openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); 301 openReference(this.deobfuscator.getMapper().deobfuscate(this.referenceStack.pop()));
297 } 302 }
298 } 303 }
299 304
@@ -313,13 +318,13 @@ public class GuiController {
313 refreshCurrentClass(null); 318 refreshCurrentClass(null);
314 } 319 }
315 320
316 private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { 321 private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> obfReference) {
317 if (this.currentObfClass != null) { 322 if (this.currentObfClass != null) {
318 deobfuscate(this.currentObfClass, obfReference); 323 deobfuscate(this.currentObfClass, obfReference);
319 } 324 }
320 } 325 }
321 326
322 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { 327 private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry<?>, Entry<?>> obfReference) {
323 328
324 this.gui.setSource("(deobfuscating...)"); 329 this.gui.setSource("(deobfuscating...)");
325 330
@@ -327,7 +332,7 @@ public class GuiController {
327 new Thread(() -> 332 new Thread(() ->
328 { 333 {
329 // decompile,deobfuscate the bytecode 334 // decompile,deobfuscate the bytecode
330 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); 335 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getFullName());
331 if (sourceTree == null) { 336 if (sourceTree == null) {
332 // decompilation of this class is not supported 337 // decompilation of this class is not supported
333 gui.setSource("Unable to find class: " + classEntry); 338 gui.setSource("Unable to find class: " + classEntry);
@@ -349,17 +354,18 @@ public class GuiController {
349 boolean remapped = false; 354 boolean remapped = false;
350 355
351 for (Token inToken : index.referenceTokens()) { 356 for (Token inToken : index.referenceTokens()) {
352 EntryReference<Entry, Entry> reference = index.getDeobfReference(inToken);
353 Token token = inToken.move(offset); 357 Token token = inToken.move(offset);
354 358
359 EntryReference<Entry<?>, Entry<?>> reference = index.getDeobfReference(inToken);
355 if (referenceIsRenameable(reference)) { 360 if (referenceIsRenameable(reference)) {
356 boolean added = false; 361 boolean added = false;
357 362
358 if (!entryHasDeobfuscatedName(reference.getNameableEntry())) { 363 if (!entryHasDeobfuscatedName(reference.getNameableEntry())) {
359 Entry obfEntry = deobfuscator.obfuscateEntry(reference.getNameableEntry()); 364 Entry<?> obfEntry = deobfuscator.getMapper().obfuscate(reference.getNameableEntry());
360 if (obfEntry instanceof FieldEntry) { 365 if (obfEntry instanceof FieldEntry) {
361 for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { 366 for (EnigmaPlugin plugin : deobfuscator.getPlugins()) {
362 String proposal = plugin.proposeFieldName(obfEntry.getClassName(), obfEntry.getName(), ((FieldEntry) obfEntry).getDesc().toString()); 367 String owner = obfEntry.getContainingClass().getFullName();
368 String proposal = plugin.proposeFieldName(owner, obfEntry.getName(), ((FieldEntry) obfEntry).getDesc().toString());
363 if (proposal != null) { 369 if (proposal != null) {
364 proposedTokens.add(token); 370 proposedTokens.add(token);
365 offset += token.getRenameOffset(proposal); 371 offset += token.getRenameOffset(proposal);
@@ -411,8 +417,7 @@ public class GuiController {
411 417
412 public void modifierChange(ItemEvent event) { 418 public void modifierChange(ItemEvent event) {
413 if (event.getStateChange() == ItemEvent.SELECTED) { 419 if (event.getStateChange() == ItemEvent.SELECTED) {
414 deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem()); 420 deobfuscator.changeModifier(gui.reference.entry, (AccessModifier) event.getItem());
415 this.isDirty = true;
416 refreshCurrentClass(); 421 refreshCurrentClass();
417 } 422 }
418 } 423 }
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
deleted file mode 100644
index 92084559..00000000
--- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java
+++ /dev/null
@@ -1,42 +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.gui;
13
14import javax.swing.*;
15import java.awt.*;
16import java.awt.event.ActionListener;
17
18public class GuiTricks {
19
20 public static JLabel unboldLabel(JLabel label) {
21 Font font = label.getFont();
22 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
23 return label;
24 }
25
26 public static void deactivateButton(JButton button) {
27 button.setEnabled(false);
28 button.setText("");
29 for (ActionListener listener : button.getActionListeners()) {
30 button.removeActionListener(listener);
31 }
32 }
33
34 public static void activateButton(JButton button, String text, ActionListener newListener) {
35 button.setText(text);
36 button.setEnabled(true);
37 for (ActionListener listener : button.getActionListeners()) {
38 button.removeActionListener(listener);
39 }
40 button.addActionListener(newListener);
41 }
42}
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
deleted file mode 100644
index 34ec26ee..00000000
--- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
+++ /dev/null
@@ -1,44 +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.gui;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15
16public class ScoredClassEntry extends ClassEntry {
17
18 private static final long serialVersionUID = -8798725308554217105L;
19
20 private float score;
21
22 public ScoredClassEntry(ClassEntry other, float score) {
23 super(other);
24 this.score = score;
25 }
26
27 public float getScore() {
28 return score;
29 }
30
31 @Override
32 public int hashCode() {
33 return Float.hashCode(score) + super.hashCode();
34 }
35
36 @Override
37 public boolean equals(Object other) {
38 return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other);
39 }
40
41 public boolean equals(ScoredClassEntry other) {
42 return other != null && Float.compare(score, other.score) == 0;
43 }
44}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
index 5f048331..84fe7c88 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
@@ -12,7 +12,7 @@
12package cuchaz.enigma.gui.dialog; 12package cuchaz.enigma.gui.dialog;
13 13
14import cuchaz.enigma.Constants; 14import cuchaz.enigma.Constants;
15import cuchaz.enigma.Deobfuscator.ProgressListener; 15import cuchaz.enigma.ProgressListener;
16import cuchaz.enigma.utils.Utils; 16import cuchaz.enigma.utils.Utils;
17 17
18import javax.swing.*; 18import javax.swing.*;
@@ -81,7 +81,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable {
81 } 81 }
82 82
83 @Override 83 @Override
84 public void onProgress(int numDone, String message) { 84 public void step(int numDone, String message) {
85 this.labelText.setText(message); 85 this.labelText.setText(message);
86 this.progress.setValue(numDone); 86 this.progress.setValue(numDone);
87 87
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index 609aecb2..f4f02776 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -5,6 +5,7 @@ import cuchaz.enigma.config.Themes;
5import cuchaz.enigma.gui.Gui; 5import cuchaz.enigma.gui.Gui;
6import cuchaz.enigma.gui.dialog.AboutDialog; 6import cuchaz.enigma.gui.dialog.AboutDialog;
7import cuchaz.enigma.throwables.MappingParseException; 7import cuchaz.enigma.throwables.MappingParseException;
8import cuchaz.enigma.translation.mapping.serde.MappingFormat;
8 9
9import javax.swing.*; 10import javax.swing.*;
10import java.awt.event.InputEvent; 11import java.awt.event.InputEvent;
@@ -23,7 +24,6 @@ public class MenuBar extends JMenuBar {
23 public final JMenuItem saveMappingEnigmaDirectoryMenu; 24 public final JMenuItem saveMappingEnigmaDirectoryMenu;
24 public final JMenuItem saveMappingsSrgMenu; 25 public final JMenuItem saveMappingsSrgMenu;
25 public final JMenuItem closeMappingsMenu; 26 public final JMenuItem closeMappingsMenu;
26 public final JMenuItem rebuildMethodNamesMenu;
27 public final JMenuItem exportSourceMenu; 27 public final JMenuItem exportSourceMenu;
28 public final JMenuItem exportJarMenu; 28 public final JMenuItem exportJarMenu;
29 private final Gui gui; 29 private final Gui gui;
@@ -68,7 +68,9 @@ public class MenuBar extends JMenuBar {
68 item.addActionListener(event -> { 68 item.addActionListener(event -> {
69 if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 69 if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
70 try { 70 try {
71 this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); 71 File selectedFile = this.gui.enigmaMappingsFileChooser.getSelectedFile();
72 MappingFormat format = selectedFile.isDirectory() ? MappingFormat.ENIGMA_DIRECTORY : MappingFormat.ENIGMA_FILE;
73 this.gui.getController().openMappings(format, selectedFile.toPath());
72 } catch (IOException ex) { 74 } catch (IOException ex) {
73 throw new Error(ex); 75 throw new Error(ex);
74 } catch (MappingParseException ex) { 76 } catch (MappingParseException ex) {
@@ -85,7 +87,7 @@ public class MenuBar extends JMenuBar {
85 File file = new File(this.gui.tinyMappingsFileChooser.getDirectory() + File.separator + this.gui.tinyMappingsFileChooser.getFile()); 87 File file = new File(this.gui.tinyMappingsFileChooser.getDirectory() + File.separator + this.gui.tinyMappingsFileChooser.getFile());
86 if (file.exists()) { 88 if (file.exists()) {
87 try { 89 try {
88 this.gui.getController().openTinyMappings(file); 90 this.gui.getController().openMappings(MappingFormat.TINY_FILE, file.toPath());
89 } catch (IOException ex) { 91 } catch (IOException ex) {
90 throw new Error(ex); 92 throw new Error(ex);
91 } catch (MappingParseException ex) { 93 } catch (MappingParseException ex) {
@@ -99,11 +101,7 @@ public class MenuBar extends JMenuBar {
99 JMenuItem item = new JMenuItem("Save Mappings"); 101 JMenuItem item = new JMenuItem("Save Mappings");
100 menu.add(item); 102 menu.add(item);
101 item.addActionListener(event -> { 103 item.addActionListener(event -> {
102 try { 104 this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath());
103 this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile());
104 } catch (IOException ex) {
105 throw new Error(ex);
106 }
107 }); 105 });
108 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); 106 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
109 this.saveMappingsMenu = item; 107 this.saveMappingsMenu = item;
@@ -116,12 +114,8 @@ public class MenuBar extends JMenuBar {
116 item.addActionListener(event -> { 114 item.addActionListener(event -> {
117 // TODO: Use a specific file chooser for it 115 // TODO: Use a specific file chooser for it
118 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 116 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
119 try { 117 this.gui.getController().saveMappings(MappingFormat.ENIGMA_FILE, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath());
120 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); 118 this.saveMappingsMenu.setEnabled(true);
121 this.saveMappingsMenu.setEnabled(true);
122 } catch (IOException ex) {
123 throw new Error(ex);
124 }
125 } 119 }
126 }); 120 });
127 this.saveMappingEnigmaFileMenu = item; 121 this.saveMappingEnigmaFileMenu = item;
@@ -132,12 +126,8 @@ public class MenuBar extends JMenuBar {
132 item.addActionListener(event -> { 126 item.addActionListener(event -> {
133 // TODO: Use a specific file chooser for it 127 // TODO: Use a specific file chooser for it
134 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 128 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
135 try { 129 this.gui.getController().saveMappings(MappingFormat.ENIGMA_DIRECTORY, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath());
136 this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); 130 this.saveMappingsMenu.setEnabled(true);
137 this.saveMappingsMenu.setEnabled(true);
138 } catch (IOException ex) {
139 throw new Error(ex);
140 }
141 } 131 }
142 }); 132 });
143 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); 133 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
@@ -149,12 +139,8 @@ public class MenuBar extends JMenuBar {
149 item.addActionListener(event -> { 139 item.addActionListener(event -> {
150 // TODO: Use a specific file chooser for it 140 // TODO: Use a specific file chooser for it
151 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { 141 if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
152 try { 142 this.gui.getController().saveMappings(MappingFormat.SRG_FILE, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath());
153 this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); 143 this.saveMappingsMenu.setEnabled(true);
154 this.saveMappingsMenu.setEnabled(true);
155 } catch (IOException ex) {
156 throw new Error(ex);
157 }
158 } 144 }
159 }); 145 });
160 this.saveMappingsSrgMenu = item; 146 this.saveMappingsSrgMenu = item;
@@ -184,13 +170,6 @@ public class MenuBar extends JMenuBar {
184 } 170 }
185 menu.addSeparator(); 171 menu.addSeparator();
186 { 172 {
187 JMenuItem item = new JMenuItem("Rebuild Method Names");
188 menu.add(item);
189 item.addActionListener(event -> this.gui.getController().rebuildMethodNames());
190 this.rebuildMethodNamesMenu = item;
191 }
192 menu.addSeparator();
193 {
194 JMenuItem item = new JMenuItem("Export Source..."); 173 JMenuItem item = new JMenuItem("Export Source...");
195 menu.add(item); 174 menu.add(item);
196 item.addActionListener(event -> { 175 item.addActionListener(event -> {
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index a965a8fa..bf6b1788 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.gui.node; 12package cuchaz.enigma.gui.node;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry; 14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15 15
16import javax.swing.tree.DefaultMutableTreeNode; 16import javax.swing.tree.DefaultMutableTreeNode;
17 17
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 9eb8f8f8..ccdc9f8a 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -2,7 +2,7 @@ package cuchaz.enigma.gui.panels;
2 2
3import cuchaz.enigma.gui.ClassSelector; 3import cuchaz.enigma.gui.ClassSelector;
4import cuchaz.enigma.gui.Gui; 4import cuchaz.enigma.gui.Gui;
5import cuchaz.enigma.mapping.entry.ClassEntry; 5import cuchaz.enigma.translation.representation.entry.ClassEntry;
6 6
7import javax.swing.*; 7import javax.swing.*;
8import java.awt.*; 8import java.awt.*;
@@ -17,8 +17,8 @@ public class PanelObf extends JPanel {
17 this.gui = gui; 17 this.gui = gui;
18 18
19 Comparator<ClassEntry> obfClassComparator = (a, b) -> { 19 Comparator<ClassEntry> obfClassComparator = (a, b) -> {
20 String aname = a.getName(); 20 String aname = a.getFullName();
21 String bname = b.getName(); 21 String bname = b.getFullName();
22 if (aname.length() != bname.length()) { 22 if (aname.length() != bname.length()) {
23 return aname.length() - bname.length(); 23 return aname.length() - bname.length();
24 } 24 }
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
deleted file mode 100644
index 9c193ef4..00000000
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ /dev/null
@@ -1,627 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Maps;
15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.entry.FieldEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.MappingConflict;
19
20import java.util.ArrayList;
21import java.util.Map;
22
23// FIXME: Enigma doesn't support inner classes of inner class????!
24public class ClassMapping implements Comparable<ClassMapping> {
25
26 private String obfFullName;
27 private String obfSimpleName;
28 private String deobfName;
29 private String deobfFullName;
30 private String previousDeobfName;
31 private Map<String, ClassMapping> innerClassesByObfSimple;
32 private Map<String, ClassMapping> innerClassesByObfFull;
33 private Map<String, ClassMapping> innerClassesByDeobf;
34 private Map<String, FieldMapping> fieldsByObf;
35 private Map<String, FieldMapping> fieldsByDeobf;
36 private Map<String, MethodMapping> methodsByObf;
37 private Map<String, MethodMapping> methodsByDeobf;
38 private boolean isDirty;
39 private Mappings.EntryModifier modifier;
40
41 public ClassMapping(String obfFullName) {
42 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
43 }
44
45 public ClassMapping(String obfFullName, String deobfName) {
46 this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED);
47 }
48
49 public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) {
50 this.obfFullName = obfFullName;
51 ClassEntry classEntry = new ClassEntry(obfFullName);
52 obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName();
53 previousDeobfName = null;
54 this.deobfName = NameValidator.validateClassName(deobfName, false);
55 innerClassesByObfSimple = Maps.newHashMap();
56 innerClassesByObfFull = Maps.newHashMap();
57 innerClassesByDeobf = Maps.newHashMap();
58 fieldsByObf = Maps.newHashMap();
59 fieldsByDeobf = Maps.newHashMap();
60 methodsByObf = Maps.newHashMap();
61 methodsByDeobf = Maps.newHashMap();
62 isDirty = true;
63 this.modifier = modifier;
64 }
65
66 public static boolean isSimpleClassName(String name) {
67 return name.indexOf('/') < 0 && name.indexOf('$') < 0;
68 }
69
70 public String getObfFullName() {
71 return obfFullName;
72 }
73
74 public String getObfSimpleName() {
75 return obfSimpleName;
76 }
77
78 public String getPreviousDeobfName() {
79 return previousDeobfName;
80 }
81
82 public String getDeobfName() {
83 return deobfName;
84 }
85
86 public String getTranslatedName(TranslationDirection direction) {
87 return direction.choose(deobfName, obfFullName);
88 }
89
90 //// INNER CLASSES ////////
91
92 public void setDeobfName(String val) {
93 previousDeobfName = deobfName;
94 deobfName = NameValidator.validateClassName(val, false);
95 this.isDirty = true;
96 }
97
98 public Iterable<ClassMapping> innerClasses() {
99 assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size());
100 return innerClassesByObfSimple.values();
101 }
102
103 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict {
104 // FIXME: dirty hack, that can get into issues, but it's a temp fix!
105 if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) {
106 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName());
107 }
108 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
109 innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping);
110
111 if (classMapping.getDeobfName() != null) {
112 if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) {
113 throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
114 }
115 innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping);
116 }
117 this.isDirty = true;
118 }
119
120 public void removeInnerClassMapping(ClassMapping classMapping) {
121 innerClassesByObfFull.remove(classMapping.getObfFullName());
122 boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null;
123 assert (obfWasRemoved);
124 if (classMapping.getDeobfName() != null) {
125 boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
126 assert (deobfWasRemoved);
127 }
128 this.isDirty = true;
129 }
130
131 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) {
132 ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName());
133 if (classMapping == null) {
134 classMapping = new ClassMapping(obfInnerClass.getName());
135 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
136 boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null;
137 assert (wasAdded);
138 this.isDirty = true;
139 }
140 return classMapping;
141 }
142
143 public ClassMapping getInnerClassByObfSimple(String obfSimpleName) {
144 assert (isSimpleClassName(obfSimpleName));
145 return innerClassesByObfSimple.get(obfSimpleName);
146 }
147
148 public ClassMapping getInnerClassByDeobf(String deobfName) {
149 assert (isSimpleClassName(deobfName));
150 return innerClassesByDeobf.get(deobfName);
151 }
152
153 public ClassMapping getInnerClassByDeobfThenObfSimple(String name) {
154 ClassMapping classMapping = getInnerClassByDeobf(name);
155 if (classMapping == null) {
156 classMapping = getInnerClassByObfSimple(name);
157 }
158 return classMapping;
159 }
160
161 public String getDeobfInnerClassName(String obfSimpleName) {
162 assert (isSimpleClassName(obfSimpleName));
163 ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName);
164 if (classMapping != null) {
165 return classMapping.getDeobfName();
166 }
167 return null;
168 }
169
170 public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) {
171 ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass);
172 if (classMapping.getDeobfName() != null) {
173 boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
174 assert (wasRemoved);
175 }
176 classMapping.setDeobfName(deobfName);
177 if (deobfName != null) {
178 assert (isSimpleClassName(deobfName));
179 boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null;
180 assert (wasAdded);
181 }
182 this.isDirty = true;
183 }
184
185 public boolean hasInnerClassByObfSimple(String obfSimpleName) {
186 return innerClassesByObfSimple.containsKey(obfSimpleName);
187 }
188
189 //// FIELDS ////////
190
191 public boolean hasInnerClassByDeobf(String deobfName) {
192 return innerClassesByDeobf.containsKey(deobfName);
193 }
194
195 public Iterable<FieldMapping> fields() {
196 assert (fieldsByObf.size() == fieldsByDeobf.size());
197 return fieldsByObf.values();
198 }
199
200 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
201 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
202 }
203
204 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
205 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
206 }
207
208 public void addFieldMapping(FieldMapping fieldMapping) {
209 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
210 if (fieldsByObf.containsKey(obfKey)) {
211 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
212 }
213 if (fieldMapping.getDeobfName() != null) {
214 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
215 if (fieldsByDeobf.containsKey(deobfKey)) {
216 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
217 }
218 boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null;
219 assert (deobfWasAdded);
220 }
221 boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null;
222 assert (obfWasAdded);
223 this.isDirty = true;
224 }
225
226 public void removeFieldMapping(FieldMapping fieldMapping) {
227 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
228 assert (obfWasRemoved);
229 if (fieldMapping.getDeobfName() != null) {
230 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
231 assert (deobfWasRemoved);
232 }
233 this.isDirty = true;
234 }
235
236 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
237 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
238 }
239
240 public FieldMapping getFieldByObf(FieldEntry field) {
241 return getFieldByObf(field.getName(), field.getDesc());
242 }
243
244 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
245 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
246 }
247
248 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
249 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
250 if (fieldMapping != null) {
251 return fieldMapping.getObfName();
252 }
253 return null;
254 }
255
256 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
257 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
258 if (fieldMapping != null) {
259 return fieldMapping.getDeobfName();
260 }
261 return null;
262 }
263
264 private String getFieldKey(String name, TypeDescriptor desc) {
265 if (name == null) {
266 throw new IllegalArgumentException("name cannot be null!");
267 }
268 if (desc == null) {
269 throw new IllegalArgumentException("desc cannot be null!");
270 }
271 return name + ":" + desc;
272 }
273
274 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
275 assert (deobfName != null);
276 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
277 if (fieldMapping == null) {
278 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
279 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
280 assert (obfWasAdded);
281 } else {
282 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
283 assert (wasRemoved);
284 }
285 fieldMapping.setDeobfName(deobfName);
286 if (deobfName != null) {
287 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
288 assert (wasAdded);
289 }
290 this.isDirty = true;
291 }
292
293 //// METHODS ////////
294
295 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
296 assert (newObfName != null);
297 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
298 assert (fieldMapping != null);
299 fieldMapping.setObfName(newObfName);
300 fieldMapping.setObfDesc(newObfDesc);
301 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
302 assert (obfWasAdded);
303 this.isDirty = true;
304 }
305
306 public Iterable<MethodMapping> methods() {
307 assert (methodsByObf.size() >= methodsByDeobf.size());
308 return methodsByObf.values();
309 }
310
311 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
312 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
313 }
314
315 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
316 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
317 }
318
319 public void addMethodMapping(MethodMapping methodMapping) {
320 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
321 if (methodsByObf.containsKey(obfKey)) {
322 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
323 }
324 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
325 assert (wasAdded);
326 if (!methodMapping.isObfuscated()) {
327 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
328 if (methodsByDeobf.containsKey(deobfKey)) {
329 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
330 }
331 boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null;
332 assert (deobfWasAdded);
333 }
334 this.isDirty = true;
335 assert (methodsByObf.size() >= methodsByDeobf.size());
336 }
337
338 public void removeMethodMapping(MethodMapping methodMapping) {
339 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
340 assert (obfWasRemoved);
341 if (!methodMapping.isObfuscated()) {
342 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
343 assert (deobfWasRemoved);
344 }
345 this.isDirty = true;
346 }
347
348 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
349 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
350 }
351
352 public MethodMapping getMethodByObf(MethodEntry method) {
353 return getMethodByObf(method.getName(), method.getDesc());
354 }
355
356 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
357 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
358 }
359
360 private String getMethodKey(String name, MethodDescriptor descriptor) {
361 if (name == null) {
362 throw new IllegalArgumentException("name cannot be null!");
363 }
364 if (descriptor == null) {
365 throw new IllegalArgumentException("descriptor cannot be null!");
366 }
367 return name + descriptor;
368 }
369
370 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
371 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
372 if (methodMapping == null) {
373 methodMapping = createMethodMapping(obfName, obfDescriptor);
374 } else if (!methodMapping.isObfuscated()) {
375 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
376 assert (wasRemoved);
377 }
378 methodMapping.setDeobfName(deobfName);
379 if (deobfName != null) {
380 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
381 assert (wasAdded);
382 }
383 this.isDirty = true;
384 }
385
386 //// ARGUMENTS ////////
387
388 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
389 assert (newObfName != null);
390 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
391 assert (methodMapping != null);
392 methodMapping.setObfName(newObfName);
393 methodMapping.setObfDescriptor(newObfDescriptor);
394 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
395 assert (obfWasAdded);
396 this.isDirty = true;
397 }
398
399 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
400 assert (argumentName != null);
401 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
402 if (methodMapping == null) {
403 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
404 }
405 methodMapping.setLocalVariableName(argumentIndex, argumentName);
406 this.isDirty = true;
407 }
408
409 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
410 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
411 this.isDirty = true;
412 }
413
414 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
415 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
416 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
417 assert (wasAdded);
418 this.isDirty = true;
419 return methodMapping;
420 }
421
422 @Override
423 public String toString() {
424 StringBuilder buf = new StringBuilder();
425 buf.append(obfFullName);
426 buf.append(" <-> ");
427 buf.append(deobfName);
428 buf.append("\n");
429 buf.append("Fields:\n");
430 for (FieldMapping fieldMapping : fields()) {
431 buf.append("\t");
432 buf.append(fieldMapping.getObfName());
433 buf.append(" <-> ");
434 buf.append(fieldMapping.getDeobfName());
435 buf.append("\n");
436 }
437 buf.append("Methods:\n");
438 for (MethodMapping methodMapping : methodsByObf.values()) {
439 buf.append(methodMapping);
440 buf.append("\n");
441 }
442 buf.append("Inner Classes:\n");
443 for (ClassMapping classMapping : innerClassesByObfSimple.values()) {
444 buf.append("\t");
445 buf.append(classMapping.getObfSimpleName());
446 buf.append(" <-> ");
447 buf.append(classMapping.getDeobfName());
448 buf.append("\n");
449 }
450 return buf.toString();
451 }
452
453 @Override
454 public int compareTo(ClassMapping other) {
455 // sort by a, b, c, ... aa, ab, etc
456 if (obfFullName.length() != other.obfFullName.length()) {
457 return obfFullName.length() - other.obfFullName.length();
458 }
459 return obfFullName.compareTo(other.obfFullName);
460 }
461
462 public boolean renameObfClass(String oldObfClassName, String newObfClassName) {
463
464 // rename inner classes
465 for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) {
466 if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) {
467 boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null;
468 assert (wasRemoved);
469 boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null;
470 assert (wasAdded);
471 }
472 }
473
474 // rename field types
475 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
476 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
477 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
478 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
479 assert (wasRemoved);
480 boolean wasAdded = fieldsByObf
481 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
482 assert (wasAdded);
483 }
484 }
485
486 // rename method signatures
487 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
488 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
489 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
490 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
491 assert (wasRemoved);
492 boolean wasAdded = methodsByObf
493 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
494 assert (wasAdded);
495 }
496 }
497
498 if (obfFullName.equals(oldObfClassName)) {
499 // rename this class
500 obfFullName = newObfClassName;
501 return true;
502 }
503 this.isDirty = true;
504 return false;
505 }
506
507 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
508 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
509 return methodMapping != null && methodMapping.containsLocalVariable(name);
510 }
511
512 public ClassEntry getObfEntry() {
513 return new ClassEntry(obfFullName);
514 }
515
516 public ClassEntry getDeObfEntry() {
517 return deobfFullName != null ? new ClassEntry(deobfFullName) : null;
518 }
519
520 public boolean isObfuscated() {
521 return this.deobfName == null || this.deobfName.equals(this.obfFullName);
522 }
523
524 public String getSaveName() {
525 return this.isObfuscated() ? this.obfFullName : this.deobfName;
526 }
527
528 public boolean isDirty() {
529 return isDirty || areInnersDirty();
530 }
531
532 private boolean areInnersDirty(){
533 for (ClassMapping c : this.innerClasses()){
534 if (c.isDirty()){
535 return true;
536 }
537 }
538 return false;
539 }
540
541 public void resetDirty() {
542 this.isDirty = false;
543 }
544
545 public void markDirty() {
546 this.isDirty = true;
547 }
548
549 public Mappings.EntryModifier getModifier() {
550 return modifier;
551 }
552
553 public void setModifier(Mappings.EntryModifier modifier) {
554 if (this.modifier != modifier)
555 this.isDirty = true;
556 this.modifier = modifier;
557 }
558
559 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
560 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
561 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
562
563 if (fieldMapping.getModifier() != modifier) {
564 fieldMapping.setModifier(modifier);
565 this.isDirty = true;
566 }
567 }
568
569 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
570 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
571 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
572
573 if (methodMapping.getModifier() != modifier) {
574 methodMapping.setModifier(modifier);
575 this.isDirty = true;
576 }
577 }
578
579 // Used for tiny parsing to keep track of deobfuscate inner classes
580 public ClassMapping setDeobfInner(String deobName) {
581 this.deobfFullName = deobName;
582 return this;
583 }
584
585 public ClassMapping copy() {
586 ClassMapping copied = new ClassMapping(this.obfFullName);
587 copied.obfSimpleName= this.obfSimpleName;
588 copied.modifier = this.modifier;
589 copied.deobfFullName = this.deobfFullName;
590 copied.deobfName = this.deobfName;
591 copied.innerClassesByDeobf = this.innerClassesByDeobf;
592 copied.innerClassesByObfFull = this.innerClassesByObfFull;
593 copied.innerClassesByObfSimple = this.innerClassesByObfSimple;
594 copied.fieldsByObf = this.fieldsByObf;
595 copied.fieldsByDeobf = this.fieldsByDeobf;
596 copied.methodsByObf = this.methodsByObf;
597 copied.methodsByDeobf = this.methodsByDeobf;
598 return copied;
599 }
600
601 @Override
602 public int hashCode() {
603 return this.obfFullName.hashCode();
604 }
605
606 @Override
607 public boolean equals(Object obj) {
608 return obj instanceof ClassMapping && ((ClassMapping) obj).obfFullName.equals(this.obfFullName);
609 }
610
611 public boolean isEmpty() {
612 if (fieldsByDeobf.isEmpty() && methodsByDeobf.isEmpty() && deobfFullName == null && deobfName == null
613 && innerClassesByObfSimple.values().stream().allMatch(ClassMapping::isEmpty)) {
614
615 // check args
616 for (MethodMapping mapping : methodsByObf.values()) {
617 if (mapping.arguments().iterator().hasNext()) {
618 return false;
619 }
620 }
621
622 return true;
623 }
624
625 return false;
626 }
627}
diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
deleted file mode 100644
index 388e7ac3..00000000
--- a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
+++ /dev/null
@@ -1,371 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Map;
23import java.util.ServiceLoader;
24
25public class DirectionalTranslator implements Translator {
26 private final TranslationDirection direction;
27 private final Map<String, ClassMapping> classes;
28 private final TranslationIndex index;
29
30 public DirectionalTranslator(ReferencedEntryPool entryPool) {
31 this.direction = null;
32 this.classes = Maps.newHashMap();
33 this.index = new TranslationIndex(entryPool);
34 }
35
36 public DirectionalTranslator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
37 this.direction = direction;
38 this.classes = classes;
39 this.index = index;
40 }
41
42 public TranslationDirection getDirection() {
43 return direction;
44 }
45
46 public TranslationIndex getTranslationIndex() {
47 return index;
48 }
49
50 @Override
51 public ClassEntry getTranslatedClass(ClassEntry entry) {
52 String className;
53 if (entry.isArray()) {
54 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
55 } else {
56 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
57 }
58 return new ClassEntry(className);
59 }
60
61 @Override
62 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
63 String className;
64 if (entry.isArray()) {
65 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
66 } else {
67 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
68 }
69 Signature translatedSignature = this.getTranslatedSignature(entry.getSignature());
70 return new ClassDefEntry(className, translatedSignature, getClassModifier(entry).transform(entry.getAccess()));
71 }
72
73 private String translateClassName(ClassEntry entry) {
74 // normal classes are easy
75 ClassMapping classMapping = this.classes.get(entry.getName());
76 if (classMapping == null) {
77 return entry.getName();
78 }
79 return classMapping.getTranslatedName(direction);
80 }
81
82 private String translateInnerClassName(ClassEntry entry) {
83 // translate as much of the class chain as we can
84 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
85 String[] obfClassNames = entry.getName().split("\\$");
86 StringBuilder buf = new StringBuilder();
87 for (int i = 0; i < obfClassNames.length; i++) {
88 boolean isFirstClass = buf.length() == 0;
89 String className = null;
90 ClassMapping classMapping = mappingsChain.get(i);
91 if (classMapping != null) {
92 className = this.direction.choose(
93 classMapping.getDeobfName(),
94 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
95 );
96 }
97 if (className == null) {
98 className = obfClassNames[i];
99 }
100 if (!isFirstClass) {
101 buf.append("$");
102 }
103 buf.append(className);
104 }
105 return buf.toString();
106 }
107
108 @Override
109 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
110 String translatedName = translateFieldName(entry);
111 if (translatedName == null) {
112 translatedName = entry.getName();
113 }
114 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
115 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
116 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
117 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
118 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, translatedAccess);
119 }
120
121 @Override
122 public FieldEntry getTranslatedField(FieldEntry entry) {
123 String translatedName = translateFieldName(entry);
124 if (translatedName == null) {
125 translatedName = entry.getName();
126 }
127 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
128 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
129 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
130 }
131
132 private String translateFieldName(FieldEntry entry) {
133 // resolve the class entry
134 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry);
135 if (resolvedClassEntry != null) {
136 // look for the class
137 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
138 if (classMapping != null) {
139 // look for the field
140 FieldMapping mapping = this.direction.choose(
141 classMapping.getFieldByObf(entry.getName(), entry.getDesc()),
142 classMapping.getFieldByDeobf(entry.getName(), getTranslatedTypeDesc(entry.getDesc()))
143 );
144 if (mapping != null) {
145 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
146 }
147 }
148 }
149 return null;
150 }
151
152 @Override
153 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
154 String translatedName = translateMethodName(entry);
155 if (translatedName == null) {
156 translatedName = entry.getName();
157 }
158 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
159 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
160 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
161 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
162 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, access);
163 }
164
165 @Override
166 public MethodEntry getTranslatedMethod(MethodEntry entry) {
167 String translatedName = translateMethodName(entry);
168 if (translatedName == null) {
169 translatedName = entry.getName();
170 }
171 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
172 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
173 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
174 }
175
176 private String translateMethodName(MethodEntry entry) {
177 // resolve the class entry
178 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry);
179 if (resolvedOwner != null) {
180 // look for class
181 ClassMapping classMapping = findClassMapping(resolvedOwner);
182 if (classMapping != null) {
183 // look for the method
184 MethodMapping mapping = this.direction.choose(
185 classMapping.getMethodByObf(entry.getName(), entry.getDesc()),
186 classMapping.getMethodByDeobf(entry.getName(), getTranslatedMethodDesc(entry.getDesc()))
187 );
188 if (mapping != null) {
189 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
190 }
191 }
192 }
193 return null;
194 }
195
196 @Override
197 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
198 String translatedArgumentName = translateLocalVariableName(entry);
199 if (translatedArgumentName == null) {
200 translatedArgumentName = inheritLocalVariableName(entry);
201 }
202 if (translatedArgumentName == null) {
203 translatedArgumentName = entry.getName();
204 }
205 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
206 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
207 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName, entry.isParameter());
208 }
209
210 @Override
211 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
212 String translatedArgumentName = translateLocalVariableName(entry);
213 if (translatedArgumentName == null) {
214 translatedArgumentName = inheritLocalVariableName(entry);
215 }
216 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
217 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
218 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
219 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName != null ? translatedArgumentName : entry.getName(), entry.isParameter(), translatedTypeDesc);
220 }
221
222 @Override
223 public boolean hasClassMapping(ClassEntry entry) {
224 return classes.containsKey(entry.getName());
225 }
226
227 @Override
228 public boolean hasFieldMapping(FieldEntry entry) {
229 return translateFieldName(entry) != null;
230 }
231
232 @Override
233 public boolean hasMethodMapping(MethodEntry entry) {
234 return translateMethodName(entry) != null;
235 }
236
237 @Override
238 public boolean hasLocalVariableMapping(LocalVariableEntry entry) {
239 return translateLocalVariableName(entry) != null || inheritLocalVariableName(entry) != null;
240 }
241
242 // TODO: support not identical behavior (specific to constructor)
243 private String translateLocalVariableName(LocalVariableEntry entry) {
244 // look for identical behavior in superclasses
245 ClassEntry ownerEntry = entry.getOwnerClassEntry();
246 if (ownerEntry != null) {
247 // look for the class
248 ClassMapping classMapping = findClassMapping(ownerEntry);
249 if (classMapping != null) {
250 // look for the method
251 MethodMapping methodMapping = this.direction.choose(
252 classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()),
253 classMapping.getMethodByDeobf(entry.getMethodName(), getTranslatedMethodDesc(entry.getMethodDesc()))
254 );
255 if (methodMapping != null) {
256 int index = entry.getIndex();
257 return this.direction.choose(
258 methodMapping.getDeobfLocalVariableName(index),
259 methodMapping.getObfLocalVariableName(index)
260 );
261 }
262 }
263 }
264 return null;
265 }
266
267 private String inheritLocalVariableName(LocalVariableEntry entry) {
268 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
269 // Check in mother class for the arg
270 for (ClassEntry ancestorEntry : ancestry) {
271 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
272 if (this.index.entryExists(motherArg)) {
273 String result = translateLocalVariableName(motherArg);
274 if (result != null) {
275 return result;
276 }
277 }
278 }
279 return null;
280 }
281
282 @Override
283 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
284 return desc.remap(this::remapClass);
285 }
286
287 @Override
288 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
289 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
290 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
291 for (TypeDescriptor argument : arguments) {
292 translatedArguments.add(getTranslatedTypeDesc(argument));
293 }
294 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
295 }
296
297 @Override
298 public Signature getTranslatedSignature(Signature signature) {
299 if (signature == null) {
300 return null;
301 }
302 return signature.remap(this::remapClass);
303 }
304
305 private ClassMapping findClassMapping(ClassEntry entry) {
306 List<ClassMapping> mappingChain = getClassMappingChain(entry);
307 return mappingChain.get(mappingChain.size() - 1);
308 }
309
310 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
311
312 // get a list of all the classes in the hierarchy
313 String[] parts = entry.getName().split("\\$");
314 List<ClassMapping> mappingsChain = Lists.newArrayList();
315
316 // get mappings for the outer class
317 ClassMapping outerClassMapping = this.classes.get(parts[0]);
318 mappingsChain.add(outerClassMapping);
319
320 for (int i = 1; i < parts.length; i++) {
321
322 // get mappings for the inner class
323 ClassMapping innerClassMapping = null;
324 if (outerClassMapping != null) {
325 innerClassMapping = this.direction.choose(
326 outerClassMapping.getInnerClassByObfSimple(parts[i]),
327 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
328 );
329 }
330 mappingsChain.add(innerClassMapping);
331 outerClassMapping = innerClassMapping;
332 }
333
334 assert (mappingsChain.size() == parts.length);
335 return mappingsChain;
336 }
337
338 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
339 ClassMapping classMapping = findClassMapping(entry);
340 if (classMapping != null) {
341 return classMapping.getModifier();
342 }
343 return Mappings.EntryModifier.UNCHANGED;
344 }
345
346 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
347 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
348 if (classMapping != null) {
349 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
350 if (fieldMapping != null) {
351 return fieldMapping.getModifier();
352 }
353 }
354 return Mappings.EntryModifier.UNCHANGED;
355 }
356
357 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
358 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
359 if (classMapping != null) {
360 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
361 if (methodMapping != null) {
362 return methodMapping.getModifier();
363 }
364 }
365 return Mappings.EntryModifier.UNCHANGED;
366 }
367
368 private String remapClass(String name) {
369 return getTranslatedClass(new ClassEntry(name)).getName();
370 }
371}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
deleted file mode 100644
index 8fbe095b..00000000
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ /dev/null
@@ -1,100 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.FieldEntry;
16import cuchaz.enigma.throwables.IllegalNameException;
17
18public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
19
20 private String obfName;
21 private String deobfName;
22 private TypeDescriptor obfDesc;
23 private Mappings.EntryModifier modifier;
24
25 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
26 this.obfName = obfName;
27 this.deobfName = NameValidator.validateFieldName(deobfName);
28 this.obfDesc = obfDesc;
29 this.modifier = modifier;
30 }
31
32 @Override
33 public FieldEntry getObfEntry(ClassEntry classEntry) {
34 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
35 }
36
37 @Override
38 public String getObfName() {
39 return this.obfName;
40 }
41
42 public void setObfName(String name) {
43 try {
44 NameValidator.validateMethodName(name);
45 } catch (IllegalNameException ex) {
46 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
47 if (this.deobfName == null) {
48 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
49 setDeobfName(name + "_auto_deob");
50 }
51 }
52 this.obfName = name;
53 }
54
55 public String getDeobfName() {
56 return this.deobfName;
57 }
58
59 public void setDeobfName(String val) {
60 this.deobfName = NameValidator.validateFieldName(val);
61 }
62
63 public TypeDescriptor getObfDesc() {
64 return this.obfDesc;
65 }
66
67 public void setObfDesc(TypeDescriptor val) {
68 this.obfDesc = val;
69 }
70
71 public Mappings.EntryModifier getModifier() {
72 return modifier;
73 }
74
75 public void setModifier(Mappings.EntryModifier modifier) {
76 this.modifier = modifier;
77 }
78
79 @Override
80 public int compareTo(FieldMapping other) {
81 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
82 }
83
84 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
85 // rename obf classes in the desc
86 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
87 if (className.equals(oldObfClassName)) {
88 return newObfClassName;
89 }
90 return className;
91 });
92
93 if (!newDesc.equals(this.obfDesc)) {
94 this.obfDesc = newDesc;
95 return true;
96 }
97 return false;
98 }
99
100}
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
deleted file mode 100644
index bfe66b2c..00000000
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
+++ /dev/null
@@ -1,58 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.LocalVariableEntry;
15import cuchaz.enigma.mapping.entry.MethodEntry;
16
17public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
18
19 private int index;
20 private String name;
21
22 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public LocalVariableMapping(int index, String name) {
24 this.index = index;
25 this.name = NameValidator.validateArgumentName(name);
26 }
27
28 public LocalVariableMapping(LocalVariableMapping other) {
29 this.index = other.index;
30 this.name = other.name;
31 }
32
33 public int getIndex() {
34 return this.index;
35 }
36
37 public String getName() {
38 return this.name;
39 }
40
41 public void setName(String val) {
42 this.name = NameValidator.validateArgumentName(val);
43 }
44
45 @Deprecated
46 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
47 return new LocalVariableEntry(methodEntry, index, name);
48 }
49
50 public LocalVariableEntry getObfEntry(MethodEntry methodEntry, boolean parameter) {
51 return new LocalVariableEntry(methodEntry, index, name, parameter);
52 }
53
54 @Override
55 public int compareTo(LocalVariableMapping other) {
56 return Integer.compare(this.index, other.index);
57 }
58}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
deleted file mode 100644
index c8650795..00000000
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ /dev/null
@@ -1,268 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.api.EnigmaPlugin;
19import cuchaz.enigma.bytecode.AccessFlags;
20import cuchaz.enigma.mapping.entry.ClassEntry;
21import cuchaz.enigma.mapping.entry.MethodEntry;
22import cuchaz.enigma.throwables.MappingConflict;
23
24import java.io.File;
25import java.io.IOException;
26import java.util.*;
27import java.util.stream.Collectors;
28
29public class Mappings {
30
31 private final FormatType originMapping;
32 protected Map<String, ClassMapping> classesByObf;
33 protected Map<String, ClassMapping> classesByDeobf;
34 private Mappings previousState;
35
36 public Mappings() {
37 this(FormatType.ENIGMA_DIRECTORY);
38 }
39
40 public Mappings(FormatType originMapping) {
41 this.originMapping = originMapping;
42 this.classesByObf = Maps.newHashMap();
43 this.classesByDeobf = Maps.newHashMap();
44 }
45
46 public Collection<ClassMapping> classes() {
47 assert (this.classesByObf.size() >= this.classesByDeobf.size());
48 return this.classesByObf.values();
49 }
50
51 public void addClassMapping(ClassMapping classMapping) throws MappingConflict {
52 if (this.classesByObf.containsKey(classMapping.getObfFullName())) {
53 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName());
54 }
55 this.classesByObf.put(classMapping.getObfFullName(), classMapping);
56
57 if (classMapping.getDeobfName() != null) {
58 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) {
59 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
60 }
61 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping);
62 }
63 }
64
65 public void removeClassMapping(ClassMapping classMapping) {
66 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null;
67 assert (obfWasRemoved);
68 if (classMapping.getDeobfName() != null) {
69 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
70 assert (deobfWasRemoved);
71 }
72 }
73
74 public ClassMapping getClassByObf(ClassEntry entry) {
75 return getClassByObf(entry.getName());
76 }
77
78 public ClassMapping getClassByObf(String obfName) {
79 return this.classesByObf.get(obfName);
80 }
81
82 public ClassMapping getClassByDeobf(ClassEntry entry) {
83 return getClassByDeobf(entry.getName());
84 }
85
86 public ClassMapping getClassByDeobf(String deobfName) {
87 return this.classesByDeobf.get(deobfName);
88 }
89
90 public void setClassDeobfName(ClassMapping classMapping, String deobfName) {
91 if (classMapping.getDeobfName() != null) {
92 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
93 assert (wasRemoved);
94 }
95 classMapping.setDeobfName(deobfName);
96 if (deobfName != null) {
97 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null;
98 assert (wasAdded);
99 }
100 }
101
102 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
103 switch (direction) {
104 case DEOBFUSCATING:
105
106 return new DirectionalTranslator(direction, this.classesByObf, index);
107
108 case OBFUSCATING:
109
110 // fill in the missing deobf class entries with obf entries
111 Map<String, ClassMapping> classes = Maps.newHashMap();
112 for (ClassMapping classMapping : classes()) {
113 if (classMapping.getDeobfName() != null) {
114 classes.put(classMapping.getDeobfName(), classMapping);
115 } else {
116 classes.put(classMapping.getObfFullName(), classMapping);
117 }
118 }
119
120 // translate the translation index
121 // NOTE: this isn't actually recursive
122 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
123
124 return new DirectionalTranslator(direction, classes, deobfIndex);
125
126 default:
127 throw new Error("Invalid translation direction!");
128 }
129 }
130
131 @Override
132 public String toString() {
133 StringBuilder buf = new StringBuilder();
134 for (ClassMapping classMapping : this.classesByObf.values()) {
135 buf.append(classMapping);
136 buf.append("\n");
137 }
138 return buf.toString();
139 }
140
141 public void renameObfClass(String oldObfName, String newObfName) {
142 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> {
143 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null;
144 assert (wasRemoved);
145 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null;
146 assert (wasAdded);
147 });
148 }
149
150 public Set<String> getAllObfClassNames() {
151 final Set<String> classNames = Sets.newHashSet();
152 for (ClassMapping classMapping : classes()) {
153
154 // add the class name
155 classNames.add(classMapping.getObfFullName());
156
157 // add classes from method signatures
158 for (MethodMapping methodMapping : classMapping.methods()) {
159 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
160 if (desc.containsType()) {
161 classNames.add(desc.getTypeEntry().getClassName());
162 }
163 }
164 }
165 }
166 return classNames;
167 }
168
169 public boolean containsDeobfClass(String deobfName) {
170 return this.classesByDeobf.containsKey(deobfName);
171 }
172
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
174 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
175 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
176 }
177
178 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
179 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
180 if (classMapping != null)
181 for (FieldMapping fieldMapping : classMapping.fields())
182 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName()))
183 return true;
184
185 return false;
186 }
187
188 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
189 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
190 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
191 }
192
193 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
194 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
195 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
196 }
197
198 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
199 List<ClassMapping> mappingChain = Lists.newArrayList();
200 ClassMapping classMapping = null;
201 for (ClassEntry obfClassEntry : obfClass.getClassChain()) {
202 if (mappingChain.isEmpty()) {
203 classMapping = this.classesByObf.get(obfClassEntry.getName());
204 } else if (classMapping != null) {
205 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName());
206 }
207 mappingChain.add(classMapping);
208 }
209 return mappingChain;
210 }
211
212 public FormatType getOriginMappingFormat() {
213 return originMapping;
214 }
215
216 public void savePreviousState() {
217 this.previousState = new Mappings(this.originMapping);
218 this.previousState.classesByDeobf = new HashMap<>();
219 for (Map.Entry<String, ClassMapping> entry : this.classesByDeobf.entrySet()) {
220 this.previousState.classesByDeobf.put(entry.getKey(), entry.getValue().copy());
221 }
222 this.previousState.classesByObf = new HashMap<>();
223 for (Map.Entry<String, ClassMapping> entry : this.classesByObf.entrySet()) {
224 this.previousState.classesByObf.put(entry.getKey(), entry.getValue().copy());
225 }
226 classesByDeobf.values().forEach(ClassMapping::resetDirty);
227 classesByObf.values().forEach(ClassMapping::resetDirty);
228 }
229
230 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException {
231 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat);
232 this.savePreviousState();
233 }
234
235 public void saveSRGMappings(File file) throws IOException {
236 new MappingsSRGWriter().write(file, this);
237 }
238
239 public Mappings getPreviousState() {
240 return previousState;
241 }
242
243 public enum FormatType {
244 ENIGMA_FILE, ENIGMA_DIRECTORY, TINY_FILE, SRG_FILE
245 }
246
247 public enum EntryModifier {
248 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
249
250 public String getFormattedName() {
251 return " ACC:" + super.toString();
252 }
253
254 public AccessFlags transform(AccessFlags access) {
255 switch (this) {
256 case PUBLIC:
257 return access.setPublic();
258 case PROTECTED:
259 return access.setProtected();
260 case PRIVATE:
261 return access.setPrivate();
262 case UNCHANGED:
263 default:
264 return access;
265 }
266 }
267 }
268}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
deleted file mode 100644
index a42f255a..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ /dev/null
@@ -1,101 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry;
18import cuchaz.enigma.mapping.entry.EntryFactory;
19import cuchaz.enigma.mapping.entry.FieldEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
21
22import java.util.Map;
23
24public class MappingsChecker {
25
26 private JarIndex index;
27 private Map<ClassEntry, ClassMapping> droppedClassMappings;
28 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
29 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
30 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
31
32 public MappingsChecker(JarIndex index) {
33 this.index = index;
34 this.droppedClassMappings = Maps.newHashMap();
35 this.droppedInnerClassMappings = Maps.newHashMap();
36 this.droppedFieldMappings = Maps.newHashMap();
37 this.droppedMethodMappings = Maps.newHashMap();
38 }
39
40 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() {
41 return this.droppedClassMappings;
42 }
43
44 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() {
45 return this.droppedInnerClassMappings;
46 }
47
48 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() {
49 return this.droppedFieldMappings;
50 }
51
52 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
53 return this.droppedMethodMappings;
54 }
55
56 public void dropBrokenMappings(Mappings mappings) {
57 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) {
58 if (!checkClassMapping(classMapping)) {
59 mappings.removeClassMapping(classMapping);
60 this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping);
61 }
62 }
63 }
64
65 private boolean checkClassMapping(ClassMapping classMapping) {
66
67 // check the class
68 ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping);
69 if (!this.index.getObfClassEntries().contains(classEntry)) {
70 return false;
71 }
72
73 // check the fields
74 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
75 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping);
76 if (!this.index.containsObfField(obfFieldEntry)) {
77 classMapping.removeFieldMapping(fieldMapping);
78 this.droppedFieldMappings.put(obfFieldEntry, fieldMapping);
79 }
80 }
81
82 // check methods
83 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
84 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
85 if (!this.index.containsObfMethod(obfMethodEntry)) {
86 classMapping.removeMethodMapping(methodMapping);
87 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
88 }
89 }
90
91 // check inner classes
92 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) {
93 if (!checkClassMapping(innerClassMapping)) {
94 classMapping.removeInnerClassMapping(innerClassMapping);
95 this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping);
96 }
97 }
98
99 return true;
100 }
101}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
deleted file mode 100644
index ddbee76f..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ /dev/null
@@ -1,186 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import com.google.common.collect.Queues;
5import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException;
7
8import java.io.BufferedReader;
9import java.io.File;
10import java.io.FileInputStream;
11import java.io.IOException;
12import java.io.InputStream;
13import java.io.InputStreamReader;
14import java.util.Deque;
15import java.util.function.Supplier;
16
17public class MappingsEnigmaReader {
18
19 public Mappings read(File file) throws IOException, MappingParseException {
20 Mappings mappings;
21
22 // Multiple file
23 if (file.isDirectory()) {
24 mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY);
25 readDirectory(mappings, file);
26 } else {
27 mappings = new Mappings();
28 readFile(mappings, file);
29 }
30 return mappings;
31 }
32
33 public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException {
34 File[] files = directory.listFiles();
35 if (files != null) {
36 for (File file : files) {
37 if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping"))
38 readFile(mappings, file);
39 else if (file.isDirectory())
40 readDirectory(mappings, file.getAbsoluteFile());
41 }
42 mappings.savePreviousState();
43 } else
44 throw new IOException("Cannot access directory" + directory.getAbsolutePath());
45 }
46
47 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException {
48 return readFileStream(mappings, new FileInputStream(file), file::getAbsolutePath);
49 }
50
51 public Mappings readFileStream(Mappings mappings, InputStream stream, Supplier<String> filenameSupplier) throws IOException, MappingParseException {
52 try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, Charsets.UTF_8))) {
53 Deque<Object> mappingStack = Queues.newArrayDeque();
54
55 int lineNumber = 0;
56 String line;
57 while ((line = in.readLine()) != null) {
58 lineNumber++;
59
60 // strip comments
61 int commentPos = line.indexOf('#');
62 if (commentPos >= 0) {
63 line = line.substring(0, commentPos);
64 }
65
66 // skip blank lines
67 if (line.trim().length() <= 0) {
68 continue;
69 }
70
71 // get the indent of this line
72 int indent = 0;
73 for (int i = 0; i < line.length(); i++) {
74 if (line.charAt(i) != '\t') {
75 break;
76 }
77 indent++;
78 }
79
80 // handle stack pops
81 while (indent < mappingStack.size()) {
82 mappingStack.pop();
83 }
84
85 String[] parts = line.trim().split("\\s");
86 try {
87 // read the first token
88 String token = parts[0];
89
90 if (token.equalsIgnoreCase("CLASS")) {
91 ClassMapping classMapping;
92 if (indent <= 0) {
93 // outer class
94 classMapping = readClass(parts, false);
95 mappings.addClassMapping(classMapping);
96 } else {
97
98 // inner class
99 if (!(mappingStack.peek() instanceof ClassMapping)) {
100 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected CLASS entry here!");
101 }
102
103 classMapping = readClass(parts, true);
104 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping);
105 }
106 mappingStack.push(classMapping);
107 } else if (token.equalsIgnoreCase("FIELD")) {
108 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
109 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected FIELD entry here!");
110 }
111 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts));
112 } else if (token.equalsIgnoreCase("METHOD")) {
113 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
114 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected METHOD entry here!");
115 }
116 MethodMapping methodMapping = readMethod(parts);
117 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping);
118 mappingStack.push(methodMapping);
119 } else if (token.equalsIgnoreCase("ARG")) {
120 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
121 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected ARG entry here!");
122 }
123 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts));
124 }
125 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
126 throw new MappingParseException(filenameSupplier, lineNumber, "Malformed line:\n" + line);
127 } catch (MappingConflict e) {
128 throw new MappingParseException(filenameSupplier, lineNumber, e.getMessage());
129 }
130 }
131 return mappings;
132 }
133 }
134
135 private LocalVariableMapping readArgument(String[] parts) {
136 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
137 }
138
139 private ClassMapping readClass(String[] parts, boolean makeSimple) {
140 if (parts.length == 2) {
141 return new ClassMapping(parts[1]);
142 } else if (parts.length == 3) {
143 boolean access = parts[2].startsWith("ACC:");
144 ClassMapping mapping;
145 if (access)
146 mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4)));
147 else
148 mapping = new ClassMapping(parts[1], parts[2]);
149
150 return mapping;
151 } else if (parts.length == 4)
152 return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4)));
153 return null;
154 }
155
156 /* TEMP */
157 protected FieldMapping readField(String[] parts) {
158 FieldMapping mapping = null;
159 if (parts.length == 4) {
160 boolean access = parts[3].startsWith("ACC:");
161 if (access)
162 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
163 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
164 else
165 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
166 } else if (parts.length == 5)
167 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
168 return mapping;
169 }
170
171 private MethodMapping readMethod(String[] parts) {
172 MethodMapping mapping = null;
173 if (parts.length == 3)
174 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
175 else if (parts.length == 4) {
176 boolean access = parts[3].startsWith("ACC:");
177 if (access)
178 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
179 else
180 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
181 } else if (parts.length == 5)
182 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
183 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
184 return mapping;
185 }
186}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
deleted file mode 100644
index e3302b14..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ /dev/null
@@ -1,160 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Charsets;
15
16import java.io.*;
17import java.util.*;
18
19public class MappingsEnigmaWriter {
20
21 public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException {
22 if (!isDirectoryFormat) {
23 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8));
24 write(outputWriter, mappings);
25 outputWriter.close();
26 } else
27 writeAsDirectory(out, mappings);
28 }
29
30 public void writeAsDirectory(File target, Mappings mappings) throws IOException {
31 if (!target.exists() && !target.mkdirs())
32 throw new IOException("Cannot create mapping directory!");
33
34 Mappings previousState = mappings.getPreviousState();
35 for (ClassMapping classMapping : sorted(mappings.classes())) {
36 File result = new File(target, classMapping.getSaveName() + ".mapping");
37
38 if (!classMapping.isDirty()) {
39 continue;
40 }
41
42 if (classMapping.isEmpty()) {
43 if (result.exists()) {
44 result.delete();
45 }
46 continue;
47 }
48
49 if (previousState != null) {
50 ClassMapping previousClass = previousState.classesByObf.get(classMapping.getObfFullName());
51 File previousFile;
52 if (previousClass != null) {
53 previousFile = new File(target, previousClass.getSaveName() + ".mapping");
54 } else {
55 previousFile = new File(target, classMapping.getObfFullName() + ".mapping");
56 }
57 if (previousFile.exists() && !previousFile.delete()) {
58 System.err.println("Failed to delete old class mapping " + previousFile.getName());
59 }
60 }
61
62 File packageFile = result.getParentFile();
63 if (!packageFile.exists()) {
64 packageFile.mkdirs();
65 }
66 result.createNewFile();
67
68 try (PrintWriter outputWriter = new PrintWriter(new BufferedWriter(new FileWriter(result)))) {
69 write(outputWriter, classMapping, 0);
70 }
71 }
72
73 // Remove dropped mappings
74 if (previousState != null) {
75 Set<ClassMapping> droppedClassMappings = new HashSet<>(previousState.classes());
76 droppedClassMappings.removeAll(mappings.classes());
77 for (ClassMapping droppedMapping : droppedClassMappings) {
78 File result = new File(target, droppedMapping.getSaveName() + ".mapping");
79 if (!result.exists()) {
80 continue;
81 }
82 if (!result.delete()) {
83 System.err.println("Failed to delete dropped class mapping " + result.getName());
84 }
85 }
86 }
87 }
88
89 public void write(PrintWriter out, Mappings mappings) throws IOException {
90 for (ClassMapping classMapping : sorted(mappings.classes())) {
91 write(out, classMapping, 0);
92 }
93 }
94
95 protected void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
96 if (classMapping.getDeobfName() == null) {
97 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(),
98 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
99 } else {
100 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(),
101 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
102 }
103
104 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
105 write(out, innerClassMapping, depth + 1);
106 }
107
108 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
109 write(out, fieldMapping, depth + 1);
110 }
111
112 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
113 write(out, methodMapping, depth + 1);
114 }
115 }
116
117 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
118 if (fieldMapping.getDeobfName() == null)
119 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
120 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
121 else
122 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(),
123 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
124 }
125
126 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
127 if (methodMapping.isObfuscated()) {
128 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
129 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
130 } else {
131 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(),
132 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
133 }
134
135 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
136 write(out, localVariableMapping, depth + 1);
137 }
138 }
139
140 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
141 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
142 }
143
144 protected <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
145 List<T> out = new ArrayList<>();
146 for (T t : classes) {
147 out.add(t);
148 }
149 Collections.sort(out);
150 return out;
151 }
152
153 private String getIndent(int depth) {
154 StringBuilder buf = new StringBuilder();
155 for (int i = 0; i < depth; i++) {
156 buf.append("\t");
157 }
158 return buf.toString();
159 }
160}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
deleted file mode 100644
index 8ef4f12b..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ /dev/null
@@ -1,365 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.mapping.entry.*;
17import cuchaz.enigma.throwables.IllegalNameException;
18import cuchaz.enigma.throwables.MappingConflict;
19
20import java.io.IOException;
21import java.io.ObjectOutputStream;
22import java.io.OutputStream;
23import java.util.List;
24import java.util.Set;
25import java.util.zip.GZIPOutputStream;
26
27public class MappingsRenamer {
28
29 private final JarIndex index;
30 private final ReferencedEntryPool entryPool;
31 private Mappings mappings;
32
33 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
34 this.index = index;
35 this.mappings = mappings;
36 this.entryPool = entryPool;
37 }
38
39 public void setMappings(Mappings mappings) {
40 this.mappings = mappings;
41 }
42
43 public void setClassName(ClassEntry obf, String deobfName) {
44
45 deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass());
46
47 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
48 if (mappingChain.size() == 1) {
49
50 if (deobfName != null) {
51 // make sure we don't rename to an existing obf or deobf class
52 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
53 throw new IllegalNameException(deobfName, "There is already a class with that name");
54 }
55 }
56
57 ClassMapping classMapping = mappingChain.get(0);
58 mappings.setClassDeobfName(classMapping, deobfName);
59
60 } else {
61
62 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
63
64 if (deobfName != null) {
65 // make sure we don't rename to an existing obf or deobf inner class
66 if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) {
67 throw new IllegalNameException(deobfName, "There is already a class with that name");
68 }
69 }
70
71 outerClassMapping.setInnerClassName(obf, deobfName);
72 }
73 }
74
75 public void removeClassMapping(ClassEntry obf) {
76 setClassName(obf, null);
77 }
78
79 public void markClassAsDeobfuscated(ClassEntry obf) {
80 String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName();
81 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
82 if (mappingChain.size() == 1) {
83 ClassMapping classMapping = mappingChain.get(0);
84 mappings.setClassDeobfName(classMapping, deobfName);
85 } else {
86 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
87 outerClassMapping.setInnerClassName(obf, deobfName);
88 }
89 }
90
91 public void setFieldName(FieldEntry obf, String deobfName) {
92 deobfName = NameValidator.validateFieldName(deobfName);
93 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
94 ClassEntry definedClass = null;
95 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) {
96 definedClass = obf.getOwnerClassEntry();
97 } else {
98 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
99 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
100 definedClass = ancestorEntry;
101 break;
102 }
103 }
104 }
105
106 if (definedClass != null) {
107 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
108 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
109 if (className == null)
110 className = definedClass.getClassName();
111 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
112 }
113
114 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
115 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
116 }
117
118 public void removeFieldMapping(FieldEntry obf) {
119 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
120 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
121 }
122
123 public void markFieldAsDeobfuscated(FieldEntry obf) {
124 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
125 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
126 }
127
128 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
129 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
130
131 // TODO: Verify if I don't break things
132 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
133 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
134 || index.containsObfMethod(targetEntry)) {
135 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
136 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
137 if (deobfClassName == null) {
138 deobfClassName = entry.getClassName();
139 }
140 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
141 }
142
143 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
144 validateMethodTreeName(entry.updateOwnership(child), deobfName);
145 }
146 }
147
148 public void setMethodTreeName(MethodEntry obf, String deobfName) {
149 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf);
150
151 deobfName = NameValidator.validateMethodName(deobfName);
152 for (MethodEntry entry : implementations) {
153 validateMethodTreeName(entry, deobfName);
154 }
155
156 for (MethodEntry entry : implementations) {
157 setMethodName(entry, deobfName);
158 }
159 }
160
161 public void setMethodName(MethodEntry obf, String deobfName) {
162 deobfName = NameValidator.validateMethodName(deobfName);
163 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
164 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
165
166 // TODO: Verify if I don't break things
167 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
168 || index.containsObfMethod(targetEntry)) {
169 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
170 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
171 if (deobfClassName == null) {
172 deobfClassName = obf.getClassName();
173 }
174 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
175 }
176
177 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
178 }
179
180 public void removeMethodTreeMapping(MethodEntry obf) {
181 index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping);
182 }
183
184 public void removeMethodMapping(MethodEntry obf) {
185 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
186 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
187 }
188
189 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
190 index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated);
191 }
192
193 public void markMethodAsDeobfuscated(MethodEntry obf) {
194 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
195 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
196 }
197
198 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
199 MethodEntry obfMethod = obf.getOwnerEntry();
200 if (!obf.isParameter()) {
201 setLocalVariableName(obf, deobfName);
202 return;
203 }
204
205 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
206 for (MethodEntry entry : implementations) {
207 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
208 if (classMapping != null) {
209 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
210 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
211 // TODO: Verify if I don't break things
212 if (mapping != null) {
213 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
214 if (localVariableMapping.getIndex() != obf.getIndex()) {
215 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
216 || localVariableMapping.getName().equals(deobfName)) {
217 throw new IllegalNameException(deobfName, "There is already an argument with that name");
218 }
219 }
220 }
221 }
222 }
223 }
224
225 for (MethodEntry entry : implementations) {
226 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName(), obf.isParameter()), deobfName);
227 }
228 }
229
230 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
231 deobfName = NameValidator.validateArgumentName(deobfName);
232 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
233 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
234 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
235 // TODO: Verify if I don't break things
236 if (mapping != null) {
237 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
238 if (localVariableMapping.getIndex() != obf.getIndex()) {
239 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
240 || localVariableMapping.getName().equals(deobfName)) {
241 throw new IllegalNameException(deobfName, "There is already an argument with that name");
242 }
243 }
244 }
245 }
246
247 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
248 }
249
250 public void removeLocalVariableMapping(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
253 }
254
255 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
256 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
257 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
258 }
259
260 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
261 classMapping.removeFieldMapping(fieldMapping);
262 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
263 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
264 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
265 targetClassMapping.addFieldMapping(fieldMapping);
266 return true;
267 } else {
268 System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName());
269 }
270 }
271 return false;
272 }
273
274 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
275 classMapping.removeMethodMapping(methodMapping);
276 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
277 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
278 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
279 targetClassMapping.addMethodMapping(methodMapping);
280 return true;
281 } else {
282 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
283 }
284 }
285 return false;
286 }
287
288 public void write(OutputStream out) throws IOException {
289 // TEMP: just use the object output for now. We can find a more efficient storage format later
290 GZIPOutputStream gzipout = new GZIPOutputStream(out);
291 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
292 oout.writeObject(this);
293 gzipout.finish();
294 }
295
296 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) {
297 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry);
298 return mappingChain.get(mappingChain.size() - 1);
299 }
300
301 private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) {
302 List<ClassEntry> classChain = obfClassEntry.getClassChain();
303 List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry);
304 for (int i = 0; i < classChain.size(); i++) {
305 ClassEntry classEntry = classChain.get(i);
306 ClassMapping classMapping = mappingChain.get(i);
307 if (classMapping == null) {
308
309 // create it
310 classMapping = new ClassMapping(classEntry.getName());
311 mappingChain.set(i, classMapping);
312
313 // add it to the right parent
314 try {
315 if (i == 0) {
316 mappings.addClassMapping(classMapping);
317 } else {
318 mappingChain.get(i - 1).addInnerClassMapping(classMapping);
319 }
320 } catch (MappingConflict mappingConflict) {
321 mappingConflict.printStackTrace();
322 }
323 }
324 }
325 return mappingChain;
326 }
327
328 public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry);
330 classMapping.setModifier(modifier);
331 }
332
333 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
335 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
336 }
337
338 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
339 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
340 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
341 }
342
343 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
344 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
345 return classMapping.getModifier();
346 }
347
348 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
349 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
350 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
351 if (fieldMapping == null) {
352 return Mappings.EntryModifier.UNCHANGED;
353 }
354 return fieldMapping.getModifier();
355 }
356
357 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
358 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
359 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
360 if (methodMapping == null) {
361 return Mappings.EntryModifier.UNCHANGED;
362 }
363 return methodMapping.getModifier();
364 }
365}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
deleted file mode 100644
index 32f0ee9f..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ /dev/null
@@ -1,80 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.analysis.TranslationIndex;
5import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
6
7import java.io.*;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.List;
11
12/**
13 * Created by Mark on 11/08/2016.
14 */
15public class MappingsSRGWriter {
16
17 public void write(File file, Mappings mappings) throws IOException {
18 if (file.exists()) {
19 file.delete();
20 }
21 file.createNewFile();
22
23 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
24
25 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
26 List<String> fieldMappings = new ArrayList<>();
27 List<String> methodMappings = new ArrayList<>();
28 for (ClassMapping classMapping : sorted(mappings.classes())) {
29 if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) {
30 continue;
31 }
32 writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName());
33 writer.write(System.lineSeparator());
34 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
35 if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) {
36 continue;
37 }
38 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName();
39 String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName();
40 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName());
41 writer.write(System.lineSeparator());
42 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) {
43 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName());
44 }
45
46 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
47 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
48 }
49 }
50
51 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
52 fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName());
53 }
54
55 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
56 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
57 }
58 }
59 for (String fd : fieldMappings) {
60 writer.write(fd);
61 writer.write(System.lineSeparator());
62 }
63
64 for (String md : methodMappings) {
65 writer.write(md);
66 writer.write(System.lineSeparator());
67 }
68
69 writer.close();
70 }
71
72 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
73 List<T> out = new ArrayList<>();
74 for (T t : classes) {
75 out.add(t);
76 }
77 Collections.sort(out);
78 return out;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
deleted file mode 100644
index 756ac432..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ /dev/null
@@ -1,130 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import com.google.common.collect.Maps;
5import cuchaz.enigma.mapping.entry.ClassEntry;
6import cuchaz.enigma.throwables.MappingConflict;
7import cuchaz.enigma.throwables.MappingParseException;
8
9import java.io.File;
10import java.io.IOException;
11import java.nio.file.Files;
12import java.util.ArrayList;
13import java.util.List;
14import java.util.Map;
15
16public class MappingsTinyReader {
17 public ClassMapping readClass(String[] parts) {
18 // Extract the inner naming of the deob form if it have one
19 String deobName = parts[2].contains("$") ? parts[2].substring(parts[2].lastIndexOf('$') + 1) : parts[2];
20 return new ClassMapping(parts[1], deobName).setDeobfInner(parts[2]);
21 }
22
23 public FieldMapping readField(String[] parts) {
24 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
25 }
26
27 public MethodMapping readMethod(String[] parts) {
28 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
29 }
30
31 public Mappings read(File file) throws IOException, MappingParseException {
32 Mappings mappings = new Mappings(Mappings.FormatType.TINY_FILE);
33 List<String> lines = Files.readAllLines(file.toPath(), Charsets.UTF_8);
34 Map<String, ClassMapping> classMappingMap = Maps.newHashMap();
35 lines.remove(0); // TODO: use the header
36 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
37 String line = lines.get(lineNumber);
38 String[] parts = line.split("\t");
39 try {
40 String token = parts[0];
41 ClassMapping classMapping;
42 switch (token) {
43 case "CLASS":
44
45 // Check for orphan created by field or method entries. It shouldn't be possible but I prefer to handle this case
46 if (classMappingMap.containsKey(parts[1])) {
47 classMapping = classMappingMap.get(parts[1]);
48
49 // We have the full deob name, Enigma only support simple class name so we extract it.
50 String deobName = parts[2].contains("$") ?
51 parts[2].substring(parts[2].lastIndexOf('$') + 1) :
52 parts[2];
53
54 // Add full deob name to the class mapping to handle inner class after this loop
55 classMappingMap.put(parts[2], classMapping.setDeobfInner(parts[2]));
56 classMapping.setDeobfName(deobName);
57
58 // Avoid to make the mapping dirty directly at the startup
59 classMapping.resetDirty();
60 } else
61 classMapping = readClass(parts);
62 classMappingMap.put(parts[1], classMapping);
63 break;
64 case "FIELD":
65 // We can have missing classes mappings because they don't have a ob name, so we create it and use it
66 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
67 classMapping.addFieldMapping(readField(parts));
68 break;
69 case "METHOD":
70 // We can have missing classes mappings because they don't have a ob name, so we create it and use it
71 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
72 classMapping.addMethodMapping(readMethod(parts));
73 break;
74 case "MTH-ARG":
75 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
76 classMapping.setArgumentName(parts[3], new MethodDescriptor(parts[2]), Integer.parseInt(parts[4]), parts[5]);
77 break;
78 default:
79 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !");
80 }
81 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
82 ex.printStackTrace();
83 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
84 }
85 }
86
87 List<ClassMapping> toRegister = new ArrayList<>(classMappingMap.values());
88
89 // After having completely parsed the file, we need to register it to the real mapping
90 for (ClassMapping classMapping : toRegister) {
91 ClassEntry obEntry = classMapping.getObfEntry();
92 ClassEntry deobEntry = classMapping.getDeObfEntry();
93 try {
94 if (obEntry.isInnerClass()) {
95 ClassMapping parent = classMappingMap.get(obEntry.getOuterClassName());
96 // Inner class can miss their parent... So we create it and add it to the mappings
97 if (parent == null) {
98 parent = new ClassMapping(obEntry.getOuterClassName()); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
99 classMappingMap.put(obEntry.getOuterClassName(), parent);
100 mappings.addClassMapping(parent);
101 }
102 // Add the inner class to the parent
103 parent.addInnerClassMapping(classMapping);
104 }
105 // obf class can become deobf inner classs, manage this case.
106 else if (deobEntry != null && deobEntry.isInnerClass()) {
107 String outerClassName = deobEntry.getOuterClassName();
108 ClassMapping parent = classMappingMap.get(outerClassName);
109
110 // Only the inner is deob??? Okay
111 if (parent == null) {
112 parent = classMappingMap.get(outerClassName);
113 if (parent == null) {
114 parent = new ClassMapping(outerClassName); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
115 classMappingMap.put(outerClassName, parent);
116 mappings.addClassMapping(parent);
117 }
118 }
119 parent.addInnerClassMapping(classMapping);
120 } else
121 mappings.addClassMapping(classMapping);
122 } catch (MappingConflict e) {
123 throw new MappingParseException(file, -1, e.getMessage());
124 }
125 }
126 lines.clear();
127 classMappingMap.clear();
128 return mappings;
129 }
130}
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
deleted file mode 100644
index 6effb91f..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ /dev/null
@@ -1,21 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.Entry;
16
17public interface MemberMapping<T extends Entry> {
18 T getObfEntry(ClassEntry classEntry);
19
20 String getObfName();
21}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
deleted file mode 100644
index 2f10144e..00000000
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ /dev/null
@@ -1,210 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.IllegalNameException;
19import cuchaz.enigma.throwables.MappingConflict;
20
21import java.util.Map;
22
23public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
24
25 private String obfName;
26 private String deobfName;
27 private MethodDescriptor obfDescriptor;
28 private Map<Integer, LocalVariableMapping> localVariables;
29 private Mappings.EntryModifier modifier;
30
31 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
32 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
33 }
34
35 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
36 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
37 }
38
39 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
40 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
41 Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null");
42 this.obfName = obfName;
43 this.deobfName = NameValidator.validateMethodName(deobfName);
44 this.obfDescriptor = obfDescriptor;
45 this.localVariables = Maps.newTreeMap();
46 this.modifier = modifier;
47 }
48
49 public MethodMapping(MethodMapping other, Translator translator) {
50 this.obfName = other.obfName;
51 this.deobfName = other.deobfName;
52 this.modifier = other.modifier;
53 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
54 this.localVariables = Maps.newTreeMap();
55 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
56 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
57 }
58 }
59
60 @Override
61 public String getObfName() {
62 return this.obfName;
63 }
64
65 public void setObfName(String name) {
66 try {
67 NameValidator.validateMethodName(name);
68 } catch (IllegalNameException ex) {
69 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
70 if (this.deobfName == null) {
71 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
72 setDeobfName(name + "_auto_deob");
73 }
74 }
75 this.obfName = name;
76 }
77
78 public String getDeobfName() {
79 if (deobfName == null) {
80 return obfName;
81 }
82 return this.deobfName;
83 }
84
85 public void setDeobfName(String val) {
86 this.deobfName = NameValidator.validateMethodName(val);
87 }
88
89 public MethodDescriptor getObfDesc() {
90 return this.obfDescriptor;
91 }
92
93 public void setObfDescriptor(MethodDescriptor val) {
94 this.obfDescriptor = val;
95 }
96
97 public Iterable<LocalVariableMapping> arguments() {
98 return this.localVariables.values();
99 }
100
101 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
102 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
103 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
104 }
105 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
106 }
107
108 public String getObfLocalVariableName(int index) {
109 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
110 if (localVariableMapping != null) {
111 return localVariableMapping.getName();
112 }
113
114 return null;
115 }
116
117 public String getDeobfLocalVariableName(int index) {
118 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
119 if (localVariableMapping != null) {
120 return localVariableMapping.getName();
121 }
122
123 return null;
124 }
125
126 public void setLocalVariableName(int index, String name) {
127 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
128 if (localVariableMapping == null) {
129 localVariableMapping = new LocalVariableMapping(index, name);
130 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
131 assert (wasAdded);
132 } else {
133 localVariableMapping.setName(name);
134 }
135 }
136
137 public void removeLocalVariableName(int index) {
138 boolean wasRemoved = this.localVariables.remove(index) != null;
139 assert (wasRemoved);
140 }
141
142 @Override
143 public String toString() {
144 StringBuilder buf = new StringBuilder();
145 buf.append("\t");
146 buf.append(this.obfName);
147 buf.append(" <-> ");
148 buf.append(this.deobfName);
149 buf.append("\n");
150 buf.append("\t");
151 buf.append(this.obfDescriptor);
152 buf.append("\n");
153 buf.append("\tLocal Variables:\n");
154 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
155 buf.append("\t\t");
156 buf.append(localVariableMapping.getIndex());
157 buf.append(" -> ");
158 buf.append(localVariableMapping.getName());
159 buf.append("\n");
160 }
161 return buf.toString();
162 }
163
164 @Override
165 public int compareTo(MethodMapping other) {
166 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
167 }
168
169 public boolean containsLocalVariable(String name) {
170 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
171 if (localVariableMapping.getName().equals(name)) {
172 return true;
173 }
174 }
175 return false;
176 }
177
178 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
179 // rename obf classes in the signature
180 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
181 if (className.equals(oldObfClassName)) {
182 return newObfClassName;
183 }
184 return className;
185 });
186
187 if (!newDescriptor.equals(this.obfDescriptor)) {
188 this.obfDescriptor = newDescriptor;
189 return true;
190 }
191 return false;
192 }
193
194 @Override
195 public MethodEntry getObfEntry(ClassEntry classEntry) {
196 return new MethodEntry(classEntry, this.obfName, this.obfDescriptor);
197 }
198
199 public Mappings.EntryModifier getModifier() {
200 return modifier;
201 }
202
203 public void setModifier(Mappings.EntryModifier modifier) {
204 this.modifier = modifier;
205 }
206
207 public boolean isObfuscated() {
208 return deobfName == null || deobfName.equals(obfName);
209 }
210}
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
deleted file mode 100644
index a9ff1cbb..00000000
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ /dev/null
@@ -1,109 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.*;
15import org.objectweb.asm.Handle;
16import org.objectweb.asm.Type;
17
18public interface Translator {
19 ClassEntry getTranslatedClass(ClassEntry entry);
20
21 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
22
23 FieldEntry getTranslatedField(FieldEntry entry);
24
25 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
26
27 MethodEntry getTranslatedMethod(MethodEntry entry);
28
29 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
30
31 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
32
33 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
34
35 boolean hasClassMapping(ClassEntry entry);
36
37 boolean hasFieldMapping(FieldEntry entry);
38
39 boolean hasMethodMapping(MethodEntry entry);
40
41 boolean hasLocalVariableMapping(LocalVariableEntry entry);
42
43 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
44
45 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
46
47 Signature getTranslatedSignature(Signature signature);
48
49 default Type getTranslatedType(Type type) {
50 String descString = type.getDescriptor();
51 switch (type.getSort()) {
52 case Type.OBJECT: {
53 ClassEntry classEntry = new ClassEntry(type.getInternalName());
54 return Type.getObjectType(getTranslatedClass(classEntry).getName());
55 }
56 case Type.ARRAY: {
57 TypeDescriptor descriptor = new TypeDescriptor(descString);
58 return Type.getType(getTranslatedTypeDesc(descriptor).toString());
59 }
60 case Type.METHOD: {
61 MethodDescriptor descriptor = new MethodDescriptor(descString);
62 return Type.getMethodType(getTranslatedMethodDesc(descriptor).toString());
63 }
64 }
65 return type;
66 }
67
68 default Handle getTranslatedHandle(Handle handle) {
69 MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc()));
70 MethodEntry translatedMethod = getTranslatedMethod(entry);
71 ClassEntry ownerClass = translatedMethod.getOwnerClassEntry();
72 return new Handle(handle.getTag(), ownerClass.getName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface());
73 }
74
75 default Object getTranslatedValue(Object value) {
76 if (value instanceof Type) {
77 return this.getTranslatedType((Type) value);
78 } else if (value instanceof Handle) {
79 return getTranslatedHandle((Handle) value);
80 }
81 return value;
82 }
83
84 @SuppressWarnings("unchecked")
85 default <T extends Entry> T getTranslatedEntry(T entry) {
86 if (entry instanceof ClassDefEntry) {
87 return (T) getTranslatedClassDef((ClassDefEntry) entry);
88 } else if (entry instanceof ClassEntry) {
89 return (T) getTranslatedClass((ClassEntry) entry);
90 } else if (entry instanceof FieldDefEntry) {
91 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
92 } else if (entry instanceof MethodDefEntry) {
93 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
94 } else if (entry instanceof FieldEntry) {
95 return (T) getTranslatedField((FieldEntry) entry);
96 } else if (entry instanceof MethodEntry) {
97 return (T) getTranslatedMethod((MethodEntry) entry);
98 } else if (entry instanceof LocalVariableDefEntry) {
99 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
100 } else if (entry instanceof LocalVariableEntry) {
101 return (T) getTranslatedVariable((LocalVariableEntry) entry);
102 } else if (entry instanceof TypeDescriptor) {
103 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
104 } else if (entry instanceof MethodDescriptor) {
105 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
106 }
107 throw new IllegalArgumentException("Cannot translate unknown entry type");
108 }
109}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
deleted file mode 100644
index df72e7e9..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
+++ /dev/null
@@ -1,38 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17
18public class ClassDefEntry extends ClassEntry implements DefEntry {
19 private final AccessFlags access;
20 private final Signature signature;
21
22 public ClassDefEntry(String className, Signature signature, AccessFlags access) {
23 super(className);
24 Preconditions.checkNotNull(signature, "Class signature cannot be null");
25 Preconditions.checkNotNull(access, "Class access cannot be null");
26 this.signature = signature;
27 this.access = access;
28 }
29
30 public Signature getSignature() {
31 return signature;
32 }
33
34 @Override
35 public AccessFlags getAccess() {
36 return access;
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
deleted file mode 100644
index c7958256..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
+++ /dev/null
@@ -1,175 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Lists;
16
17import java.util.List;
18
19public class ClassEntry implements Entry {
20
21 private final String name;
22
23 public ClassEntry(String className) {
24 Preconditions.checkNotNull(className, "Class name cannot be null");
25
26 if (className.indexOf('.') >= 0) {
27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
28 }
29
30 this.name = className;
31
32 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) {
33 throw new IllegalArgumentException("Inner class must not have a package: " + className);
34 }
35 }
36
37 public ClassEntry(ClassEntry other) {
38 this.name = other.name;
39 }
40
41 @Override
42 public String getName() {
43 return this.name;
44 }
45
46 @Override
47 public String getClassName() {
48 return this.name;
49 }
50
51 @Override
52 public ClassEntry getOwnerClassEntry() {
53 return this;
54 }
55
56 @Override
57 public ClassEntry updateOwnership(ClassEntry classEntry) {
58 return classEntry;
59 }
60
61 @Override
62 public int hashCode() {
63 return this.name.hashCode();
64 }
65
66 @Override
67 public boolean equals(Object other) {
68 return other instanceof ClassEntry && equals((ClassEntry) other);
69 }
70
71 public boolean equals(ClassEntry other) {
72 return other != null && this.name.equals(other.name);
73 }
74
75 @Override
76 public String toString() {
77 return this.name;
78 }
79
80 public boolean isArray() {
81 return this.name.lastIndexOf('[') >= 0;
82 }
83
84 public boolean isInnerClass() {
85 return this.name.lastIndexOf('$') >= 0;
86 }
87
88 public List<String> getClassChainNames() {
89 return Lists.newArrayList(this.name.split("\\$"));
90 }
91
92 public List<ClassEntry> getClassChain() {
93 List<ClassEntry> entries = Lists.newArrayList();
94 StringBuilder buf = new StringBuilder();
95 for (String name : getClassChainNames()) {
96 if (buf.length() > 0) {
97 buf.append("$");
98 }
99 buf.append(name);
100 entries.add(new ClassEntry(buf.toString()));
101 }
102 return entries;
103 }
104
105 public String getOutermostClassName() {
106 if (isInnerClass()) {
107 return this.name.substring(0, this.name.indexOf('$'));
108 }
109 return this.name;
110 }
111
112 public ClassEntry getOutermostClassEntry() {
113 return new ClassEntry(getOutermostClassName());
114 }
115
116 public String getOuterClassName() {
117 if (!isInnerClass()) {
118 throw new Error("This is not an inner class!");
119 }
120 return this.name.substring(0, this.name.lastIndexOf('$'));
121 }
122
123 public ClassEntry getOuterClassEntry() {
124 return new ClassEntry(getOuterClassName());
125 }
126
127 public String getInnermostClassName() {
128 if (!isInnerClass()) {
129 throw new Error("This is not an inner class!");
130 }
131 return this.name.substring(this.name.lastIndexOf('$') + 1);
132 }
133
134 public boolean isInDefaultPackage() {
135 return this.name.indexOf('/') < 0;
136 }
137
138 public String getPackageName() {
139 return getPackageName(this.name);
140 }
141
142 public String getSimpleName() {
143 int pos = this.name.lastIndexOf('/');
144 if (pos > 0) {
145 return this.name.substring(pos + 1);
146 }
147 return this.name;
148 }
149
150 public static String getPackageName(String name) {
151 int pos = name.lastIndexOf('/');
152 if (pos > 0) {
153 return name.substring(0, pos);
154 }
155 return null;
156 }
157
158 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
159 assert (classChain.contains(this));
160 StringBuilder buf = new StringBuilder();
161 for (ClassEntry chainEntry : classChain) {
162 if (buf.length() == 0) {
163 buf.append(chainEntry.getName());
164 } else {
165 buf.append("$");
166 buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName());
167 }
168
169 if (chainEntry == this) {
170 break;
171 }
172 }
173 return new ClassEntry(buf.toString());
174 }
175}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java
deleted file mode 100644
index 43ad0274..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java
+++ /dev/null
@@ -1,7 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4
5public interface DefEntry extends Entry {
6 AccessFlags getAccess();
7}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
deleted file mode 100644
index b612140f..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
+++ /dev/null
@@ -1,22 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14public interface Entry {
15 String getName();
16
17 String getClassName();
18
19 ClassEntry getOwnerClassEntry();
20
21 Entry updateOwnership(ClassEntry classEntry);
22}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
deleted file mode 100644
index 5bd159f4..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.mapping.ClassMapping;
16import cuchaz.enigma.mapping.FieldMapping;
17import cuchaz.enigma.mapping.MethodDescriptor;
18import cuchaz.enigma.mapping.MethodMapping;
19
20public class EntryFactory {
21 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
22 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
23 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
24 }
25
26 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
27 return new ClassEntry(classMapping.getObfFullName());
28 }
29
30 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
31 return new ClassEntry(classMapping.getDeobfName());
32 }
33
34 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
35 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
36 }
37
38 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
39 return new MethodEntry(classEntry, name, desc);
40 }
41
42 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
43 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
44 }
45
46 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
47 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
deleted file mode 100644
index 223410f5..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
+++ /dev/null
@@ -1,44 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17import cuchaz.enigma.mapping.TypeDescriptor;
18
19public class FieldDefEntry extends FieldEntry implements DefEntry {
20 private final AccessFlags access;
21 private final Signature signature;
22
23 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
24 super(ownerEntry, name, desc);
25 Preconditions.checkNotNull(access, "Field access cannot be null");
26 Preconditions.checkNotNull(signature, "Field signature cannot be null");
27 this.access = access;
28 this.signature = signature;
29 }
30
31 @Override
32 public AccessFlags getAccess() {
33 return access;
34 }
35
36 public Signature getSignature() {
37 return signature;
38 }
39
40 @Override
41 public FieldDefEntry updateOwnership(ClassEntry owner) {
42 return new FieldDefEntry(owner, this.name, this.desc, signature, access);
43 }
44}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
deleted file mode 100644
index b6e1554d..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
+++ /dev/null
@@ -1,77 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.TypeDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class FieldEntry implements Entry {
19
20 protected final ClassEntry ownerEntry;
21 protected final String name;
22 protected final TypeDescriptor desc;
23
24 // NOTE: this argument order is important for the MethodReader/MethodWriter
25 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
26 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
27 Preconditions.checkNotNull(name, "Field name cannot be null");
28 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
29
30 this.ownerEntry = ownerEntry;
31 this.name = name;
32 this.desc = desc;
33 }
34
35 @Override
36 public ClassEntry getOwnerClassEntry() {
37 return this.ownerEntry;
38 }
39
40 @Override
41 public String getName() {
42 return this.name;
43 }
44
45 @Override
46 public String getClassName() {
47 return this.ownerEntry.getName();
48 }
49
50 public TypeDescriptor getDesc() {
51 return this.desc;
52 }
53
54 @Override
55 public FieldEntry updateOwnership(ClassEntry owner) {
56 return new FieldEntry(owner, this.name, this.desc);
57 }
58
59 @Override
60 public int hashCode() {
61 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
62 }
63
64 @Override
65 public boolean equals(Object other) {
66 return other instanceof FieldEntry && equals((FieldEntry) other);
67 }
68
69 public boolean equals(FieldEntry other) {
70 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
71 }
72
73 @Override
74 public String toString() {
75 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
deleted file mode 100644
index d1866644..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
+++ /dev/null
@@ -1,61 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableDefEntry extends LocalVariableEntry {
13
14 protected final MethodDefEntry ownerEntry;
15 protected final TypeDescriptor desc;
16
17 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
18 this(ownerEntry, index, name, true, desc);
19 }
20
21 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, boolean parameter, TypeDescriptor desc) {
22 super(ownerEntry, index, name, parameter);
23 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
24
25 this.ownerEntry = ownerEntry;
26 this.desc = desc;
27 }
28
29 @Override
30 public MethodDefEntry getOwnerEntry() {
31 return this.ownerEntry;
32 }
33
34 public TypeDescriptor getDesc() {
35 return desc;
36 }
37
38 @Override
39 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
40 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter, desc);
41 }
42
43 @Override
44 public int hashCode() {
45 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
46 }
47
48 @Override
49 public boolean equals(Object other) {
50 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
51 }
52
53 public boolean equals(LocalVariableDefEntry other) {
54 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
55 }
56
57 @Override
58 public String toString() {
59 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
60 }
61}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
deleted file mode 100644
index 3507b252..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
+++ /dev/null
@@ -1,93 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableEntry implements Entry {
13
14 protected final MethodEntry ownerEntry;
15 protected final String name;
16 protected final int index;
17 protected final boolean parameter;
18
19 @Deprecated
20 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
21 this(ownerEntry, index, name, true);
22 }
23
24 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name, boolean parameter) {
25 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
26 Preconditions.checkNotNull(name, "Variable name cannot be null");
27 Preconditions.checkArgument(index >= 0, "Index must be positive");
28
29 this.ownerEntry = ownerEntry;
30 this.name = name;
31 this.index = index;
32 this.parameter = parameter;
33 }
34
35 public boolean isParameter() {
36 return this.parameter;
37 }
38
39 public MethodEntry getOwnerEntry() {
40 return this.ownerEntry;
41 }
42
43 public int getIndex() {
44 return index;
45 }
46
47 @Override
48 public String getName() {
49 return this.name;
50 }
51
52 @Override
53 public ClassEntry getOwnerClassEntry() {
54 return this.ownerEntry.getOwnerClassEntry();
55 }
56
57 @Override
58 public String getClassName() {
59 return this.ownerEntry.getClassName();
60 }
61
62 @Override
63 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
64 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter);
65 }
66
67 public String getMethodName() {
68 return this.ownerEntry.getName();
69 }
70
71 public MethodDescriptor getMethodDesc() {
72 return this.ownerEntry.getDesc();
73 }
74
75 @Override
76 public int hashCode() {
77 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
78 }
79
80 @Override
81 public boolean equals(Object other) {
82 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
83 }
84
85 public boolean equals(LocalVariableEntry other) {
86 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
87 }
88
89 @Override
90 public String toString() {
91 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
92 }
93}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
deleted file mode 100644
index 1abc5b12..00000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
+++ /dev/null
@@ -1,80 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.MethodDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class MethodEntry implements Entry {
19
20 protected final ClassEntry classEntry;
21 protected final String name;
22 protected final MethodDescriptor descriptor;
23
24 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
25 Preconditions.checkNotNull(classEntry, "Class cannot be null");
26 Preconditions.checkNotNull(name, "Method name cannot be null");
27 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
28
29 this.classEntry = classEntry;
30 this.name = name;
31 this.descriptor = descriptor;
32 }
33
34 @Override
35 public ClassEntry getOwnerClassEntry() {
36 return this.classEntry;
37 }
38
39 @Override
40 public String getName() {
41 return this.name;
42 }
43
44 public MethodDescriptor getDesc() {
45 return this.descriptor;
46 }
47
48 public boolean isConstructor() {
49 return name.equals("<init>") || name.equals("<clinit>");
50 }
51
52 @Override
53 public String getClassName() {
54 return this.classEntry.getName();
55 }
56
57 @Override
58 public MethodEntry updateOwnership(ClassEntry classEntry) {
59 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
60 }
61
62 @Override
63 public int hashCode() {
64 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
65 }
66
67 @Override
68 public boolean equals(Object other) {
69 return other instanceof MethodEntry && equals((MethodEntry) other);
70 }
71
72 public boolean equals(MethodEntry other) {
73 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
74 }
75
76 @Override
77 public String toString() {
78 return this.classEntry.getName() + "." + this.name + this.descriptor;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/translation/MappingTranslator.java b/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
new file mode 100644
index 00000000..529d0edb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
@@ -0,0 +1,24 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.mapping.EntryMap;
6
7public class MappingTranslator implements Translator {
8 private final EntryMap<EntryMapping> mappings;
9 private final EntryResolver resolver;
10
11 public MappingTranslator(EntryMap<EntryMapping> mappings, EntryResolver resolver) {
12 this.mappings = mappings;
13 this.resolver = resolver;
14 }
15
16 @SuppressWarnings("unchecked")
17 @Override
18 public <T extends Translatable> T translate(T translatable) {
19 if (translatable == null) {
20 return null;
21 }
22 return (T) translatable.translate(this, resolver, mappings);
23 }
24}
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
index ddc5af4b..37830535 100644
--- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
+++ b/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
@@ -9,7 +9,7 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.translation;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15 15
diff --git a/src/main/java/cuchaz/enigma/translation/Translatable.java b/src/main/java/cuchaz/enigma/translation/Translatable.java
new file mode 100644
index 00000000..0370ef13
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/Translatable.java
@@ -0,0 +1,9 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.mapping.EntryMap;
6
7public interface Translatable {
8 Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings);
9}
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
index 4bbde548..2ecb30be 100644
--- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
+++ b/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
@@ -9,7 +9,7 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.translation;
13 13
14public enum TranslationDirection { 14public enum TranslationDirection {
15 15
diff --git a/src/main/java/cuchaz/enigma/translation/Translator.java b/src/main/java/cuchaz/enigma/translation/Translator.java
new file mode 100644
index 00000000..de2003e8
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/Translator.java
@@ -0,0 +1,54 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16
17import java.util.Collection;
18import java.util.HashMap;
19import java.util.Map;
20import java.util.stream.Collectors;
21
22public interface Translator {
23 <T extends Translatable> T translate(T translatable);
24
25 default <T extends Translatable> Collection<T> translate(Collection<T> translatable) {
26 return translatable.stream()
27 .map(this::translate)
28 .collect(Collectors.toList());
29 }
30
31 default <T extends Translatable, V> Map<T, V> translateKeys(Map<T, V> translatable) {
32 Map<T, V> result = new HashMap<>(translatable.size());
33 for (Map.Entry<T, V> entry : translatable.entrySet()) {
34 result.put(translate(entry.getKey()), entry.getValue());
35 }
36 return result;
37 }
38
39 default <K extends Translatable, V extends Translatable> Map<K, V> translate(Map<K, V> translatable) {
40 Map<K, V> result = new HashMap<>(translatable.size());
41 for (Map.Entry<K, V> entry : translatable.entrySet()) {
42 result.put(translate(entry.getKey()), translate(entry.getValue()));
43 }
44 return result;
45 }
46
47 default <K extends Translatable, V extends Translatable> Multimap<K, V> translate(Multimap<K, V> translatable) {
48 Multimap<K, V> result = HashMultimap.create(translatable.size(), 1);
49 for (Map.Entry<K, Collection<V>> entry : translatable.asMap().entrySet()) {
50 result.putAll(translate(entry.getKey()), translate(entry.getValue()));
51 }
52 return result;
53 }
54}
diff --git a/src/main/java/cuchaz/enigma/translation/VoidTranslator.java b/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
new file mode 100644
index 00000000..c010833b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
@@ -0,0 +1,10 @@
1package cuchaz.enigma.translation;
2
3public enum VoidTranslator implements Translator {
4 INSTANCE;
5
6 @Override
7 public <T extends Translatable> T translate(T translatable) {
8 return translatable;
9 }
10}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java b/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
new file mode 100644
index 00000000..5b79b794
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
@@ -0,0 +1,25 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4
5public enum AccessModifier {
6 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
7
8 public String getFormattedName() {
9 return "ACC:" + super.toString();
10 }
11
12 public AccessFlags transform(AccessFlags access) {
13 switch (this) {
14 case PUBLIC:
15 return access.setPublic();
16 case PROTECTED:
17 return access.setProtected();
18 case PRIVATE:
19 return access.setPrivate();
20 case UNCHANGED:
21 default:
22 return access;
23 }
24 }
25}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
new file mode 100644
index 00000000..6af48466
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
@@ -0,0 +1,24 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.Collection;
7
8public interface EntryMap<T> {
9 void insert(Entry<?> entry, T value);
10
11 @Nullable
12 T remove(Entry<?> entry);
13
14 @Nullable
15 T get(Entry<?> entry);
16
17 default boolean contains(Entry<?> entry) {
18 return get(entry) != null;
19 }
20
21 Collection<Entry<?>> getAllEntries();
22
23 boolean isEmpty();
24}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
new file mode 100644
index 00000000..f11cdefb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
@@ -0,0 +1,30 @@
1package cuchaz.enigma.translation.mapping;
2
3import javax.annotation.Nonnull;
4
5public class EntryMapping {
6 private final String targetName;
7 private final AccessModifier accessModifier;
8
9 public EntryMapping(@Nonnull String targetName) {
10 this(targetName, AccessModifier.UNCHANGED);
11 }
12
13 public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier) {
14 this.targetName = targetName;
15 this.accessModifier = accessModifier;
16 }
17
18 @Nonnull
19 public String getTargetName() {
20 return targetName;
21 }
22
23 @Nonnull
24 public AccessModifier getAccessModifier() {
25 if (accessModifier == null) {
26 return AccessModifier.UNCHANGED;
27 }
28 return accessModifier;
29 }
30}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
new file mode 100644
index 00000000..b7d8d17e
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
@@ -0,0 +1,201 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.analysis.index.JarIndex;
4import cuchaz.enigma.translation.MappingTranslator;
5import cuchaz.enigma.translation.Translatable;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
10import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
11import cuchaz.enigma.translation.representation.entry.Entry;
12
13import javax.annotation.Nullable;
14import java.util.Collection;
15
16public class EntryRemapper {
17 private final EntryTree<EntryMapping> obfToDeobf;
18 private final DeltaTrackingTree<EntryMapping> deobfToObf;
19
20 private final JarIndex obfIndex;
21
22 private final EntryResolver obfResolver;
23 private EntryResolver deobfResolver;
24
25 private final Translator deobfuscator;
26 private Translator obfuscator;
27
28 private final MappingValidator validator;
29
30 private EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf, EntryTree<EntryMapping> deobfToObf) {
31 this.obfToDeobf = obfToDeobf;
32 this.deobfToObf = new DeltaTrackingTree<>(deobfToObf);
33
34 this.obfIndex = jarIndex;
35 this.obfResolver = jarIndex.getEntryResolver();
36
37 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver);
38 rebuildDeobfIndex();
39
40 this.validator = new MappingValidator(this.deobfToObf, deobfuscator, obfResolver);
41 }
42
43 public EntryRemapper(JarIndex jarIndex) {
44 this(jarIndex, new HashEntryTree<>(), 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 }
71
72 public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) {
73 Collection<E> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, ResolutionStrategy.RESOLVE_ROOT);
74 for (E resolvedEntry : resolvedEntries) {
75 if (deobfMapping != null) {
76 validator.validateRename(resolvedEntry, deobfMapping.getTargetName());
77 }
78
79 setObfToDeobf(resolvedEntry, deobfMapping);
80 }
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 }
90
91 public void removeByObf(Entry<?> obfuscatedEntry) {
92 mapFromObf(obfuscatedEntry, null);
93 }
94
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
124 public EntryMapping getDeobfMapping(Entry<?> entry) {
125 return obfToDeobf.get(entry);
126 }
127
128 @Nullable
129 public EntryMapping getObfMapping(Entry<?> entry) {
130 return deobfToObf.get(entry);
131 }
132
133 public boolean hasDeobfMapping(Entry<?> obfEntry) {
134 return obfToDeobf.contains(obfEntry);
135 }
136
137 public boolean hasObfMapping(Entry<?> deobfEntry) {
138 return deobfToObf.contains(deobfEntry);
139 }
140
141 public <T extends Translatable> T deobfuscate(T translatable) {
142 return deobfuscator.translate(translatable);
143 }
144
145 public <T extends Translatable> T obfuscate(T translatable) {
146 return obfuscator.translate(translatable);
147 }
148
149 public Translator getDeobfuscator() {
150 return deobfuscator;
151 }
152
153 public Translator getObfuscator() {
154 return obfuscator;
155 }
156
157 public Collection<Entry<?>> getObfEntries() {
158 return obfToDeobf.getAllEntries();
159 }
160
161 public Collection<Entry<?>> getObfRootEntries() {
162 return obfToDeobf.getRootEntries();
163 }
164
165 public Collection<Entry<?>> getDeobfEntries() {
166 return deobfToObf.getAllEntries();
167 }
168
169 public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) {
170 return obfToDeobf.getChildren(obfuscatedEntry);
171 }
172
173 public Collection<Entry<?>> getDeobfChildren(Entry<?> deobfuscatedEntry) {
174 return deobfToObf.getChildren(deobfuscatedEntry);
175 }
176
177 public EntryTree<EntryMapping> getObfToDeobf() {
178 return obfToDeobf;
179 }
180
181 public DeltaTrackingTree<EntryMapping> getDeobfToObf() {
182 return deobfToObf;
183 }
184
185 public MappingDelta takeMappingDelta() {
186 MappingDelta delta = deobfToObf.takeDelta();
187 return delta.translate(obfuscator, VoidEntryResolver.INSTANCE, deobfToObf);
188 }
189
190 public boolean isDirty() {
191 return deobfToObf.isDirty();
192 }
193
194 public EntryResolver getObfResolver() {
195 return obfResolver;
196 }
197
198 public EntryResolver getDeobfResolver() {
199 return deobfResolver;
200 }
201}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
new file mode 100644
index 00000000..521f72d0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
@@ -0,0 +1,41 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.common.collect.Streams;
4import cuchaz.enigma.analysis.EntryReference;
5import cuchaz.enigma.translation.representation.entry.Entry;
6import cuchaz.enigma.translation.representation.entry.MethodEntry;
7
8import java.util.Collection;
9import java.util.Set;
10import java.util.stream.Collectors;
11
12public interface EntryResolver {
13 <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy);
14
15 default <E extends Entry<?>> E resolveFirstEntry(E entry, ResolutionStrategy strategy) {
16 return resolveEntry(entry, strategy).stream().findFirst().orElse(entry);
17 }
18
19 default <E extends Entry<?>, C extends Entry<?>> Collection<EntryReference<E, C>> resolveReference(EntryReference<E, C> reference, ResolutionStrategy strategy) {
20 Collection<E> entry = resolveEntry(reference.entry, strategy);
21 if (reference.context != null) {
22 Collection<C> context = resolveEntry(reference.context, strategy);
23 return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference))
24 .collect(Collectors.toList());
25 } else {
26 return entry.stream()
27 .map(e -> new EntryReference<>(e, null, reference))
28 .collect(Collectors.toList());
29 }
30 }
31
32 default <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> resolveFirstReference(EntryReference<E, C> reference, ResolutionStrategy strategy) {
33 E entry = resolveFirstEntry(reference.entry, strategy);
34 C context = resolveFirstEntry(reference.context, strategy);
35 return new EntryReference<>(entry, context, reference);
36 }
37
38 Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry);
39
40 Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry);
41}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
new file mode 100644
index 00000000..1f2290a0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
@@ -0,0 +1,225 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.common.collect.Sets;
4import cuchaz.enigma.analysis.IndexTreeBuilder;
5import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
6import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
7import cuchaz.enigma.analysis.index.BridgeMethodIndex;
8import cuchaz.enigma.analysis.index.EntryIndex;
9import cuchaz.enigma.analysis.index.InheritanceIndex;
10import cuchaz.enigma.analysis.index.JarIndex;
11import cuchaz.enigma.translation.VoidTranslator;
12import cuchaz.enigma.translation.representation.AccessFlags;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.translation.representation.entry.MethodEntry;
16
17import javax.annotation.Nullable;
18import java.util.*;
19import java.util.stream.Collectors;
20
21public class IndexEntryResolver implements EntryResolver {
22 private final EntryIndex entryIndex;
23 private final InheritanceIndex inheritanceIndex;
24 private final BridgeMethodIndex bridgeMethodIndex;
25
26 private final IndexTreeBuilder treeBuilder;
27
28 public IndexEntryResolver(JarIndex index) {
29 this.entryIndex = index.getEntryIndex();
30 this.inheritanceIndex = index.getInheritanceIndex();
31 this.bridgeMethodIndex = index.getBridgeMethodIndex();
32
33 this.treeBuilder = new IndexTreeBuilder(index);
34 }
35
36 @Override
37 @SuppressWarnings("unchecked")
38 public <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy) {
39 if (entry == null) {
40 return Collections.emptySet();
41 }
42
43 Entry<ClassEntry> classChild = getClassChild(entry);
44 if (classChild != null && !(classChild instanceof ClassEntry)) {
45 AccessFlags access = entryIndex.getEntryAccess(classChild);
46
47 // If we're looking for the closest and this entry exists, we're done looking
48 if (strategy == ResolutionStrategy.RESOLVE_CLOSEST && access != null) {
49 return Collections.singleton(entry);
50 }
51
52 if (access == null || !access.isPrivate()) {
53 Collection<Entry<ClassEntry>> resolvedChildren = resolveChildEntry(classChild, strategy);
54 if (!resolvedChildren.isEmpty()) {
55 return resolvedChildren.stream()
56 .map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild))
57 .collect(Collectors.toList());
58 }
59 }
60 }
61
62 return Collections.singleton(entry);
63 }
64
65 @Nullable
66 private Entry<ClassEntry> getClassChild(Entry<?> entry) {
67 if (entry instanceof ClassEntry) {
68 return null;
69 }
70
71 // get the entry in the hierarchy that is the child of a class
72 List<Entry<?>> ancestry = entry.getAncestry();
73 for (int i = ancestry.size() - 1; i > 0; i--) {
74 Entry<?> child = ancestry.get(i);
75 Entry<ClassEntry> cast = child.castParent(ClassEntry.class);
76 if (cast != null && !(cast instanceof ClassEntry)) {
77 // we found the entry which is a child of a class, we are now able to resolve the owner of this entry
78 return cast;
79 }
80 }
81
82 return null;
83 }
84
85 private Set<Entry<ClassEntry>> resolveChildEntry(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
86 ClassEntry ownerClass = entry.getParent();
87
88 if (entry instanceof MethodEntry) {
89 MethodEntry bridgeMethod = bridgeMethodIndex.getBridgeFromAccessed((MethodEntry) entry);
90 if (bridgeMethod != null && ownerClass.equals(bridgeMethod.getParent())) {
91 Set<Entry<ClassEntry>> resolvedBridge = resolveChildEntry(bridgeMethod, strategy);
92 if (!resolvedBridge.isEmpty()) {
93 return resolvedBridge;
94 }
95 }
96 }
97
98 Set<Entry<ClassEntry>> resolvedEntries = new HashSet<>();
99
100 for (ClassEntry parentClass : inheritanceIndex.getParents(ownerClass)) {
101 Entry<ClassEntry> parentEntry = entry.withParent(parentClass);
102
103 if (strategy == ResolutionStrategy.RESOLVE_ROOT) {
104 resolvedEntries.addAll(resolveRoot(parentEntry, strategy));
105 } else {
106 resolvedEntries.addAll(resolveClosest(parentEntry, strategy));
107 }
108 }
109
110 return resolvedEntries;
111 }
112
113 private Collection<Entry<ClassEntry>> resolveRoot(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
114 // When resolving root, we want to first look for the lowest entry before returning ourselves
115 Set<Entry<ClassEntry>> parentResolution = resolveChildEntry(entry, strategy);
116
117 if (parentResolution.isEmpty()) {
118 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
119 if (parentAccess != null && !parentAccess.isPrivate()) {
120 return Collections.singleton(entry);
121 }
122 }
123
124 return parentResolution;
125 }
126
127 private Collection<Entry<ClassEntry>> resolveClosest(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
128 // When resolving closest, we want to first check if we exist before looking further down
129 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
130 if (parentAccess != null && !parentAccess.isPrivate()) {
131 return Collections.singleton(entry);
132 } else {
133 return resolveChildEntry(entry, strategy);
134 }
135 }
136
137 @Override
138 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) {
139 MethodEntry relevantMethod = entry.findAncestor(MethodEntry.class);
140 if (relevantMethod == null || !entryIndex.hasMethod(relevantMethod)) {
141 return Collections.singleton(entry);
142 }
143
144 Set<MethodEntry> equivalentMethods = resolveEquivalentMethods(relevantMethod);
145 Set<Entry<?>> equivalentEntries = new HashSet<>(equivalentMethods.size());
146
147 for (MethodEntry equivalentMethod : equivalentMethods) {
148 Entry<?> equivalentEntry = entry.replaceAncestor(relevantMethod, equivalentMethod);
149 equivalentEntries.add(equivalentEntry);
150 }
151
152 return equivalentEntries;
153 }
154
155 @Override
156 public Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry) {
157 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
158 if (access == null) {
159 throw new IllegalArgumentException("Could not find method " + methodEntry);
160 }
161
162 if (!canInherit(methodEntry, access)) {
163 return Collections.singleton(methodEntry);
164 }
165
166 Set<MethodEntry> methodEntries = Sets.newHashSet();
167 resolveEquivalentMethods(methodEntries, treeBuilder.buildMethodInheritance(VoidTranslator.INSTANCE, methodEntry));
168 return methodEntries;
169 }
170
171 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
172 MethodEntry methodEntry = node.getMethodEntry();
173 if (methodEntries.contains(methodEntry)) {
174 return;
175 }
176
177 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
178 if (flags != null && canInherit(methodEntry, flags)) {
179 // collect the entry
180 methodEntries.add(methodEntry);
181 }
182
183 // look at bridge methods!
184 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromAccessed(methodEntry);
185 while (bridgedMethod != null) {
186 methodEntries.addAll(resolveEquivalentMethods(bridgedMethod));
187 bridgedMethod = bridgeMethodIndex.getBridgeFromAccessed(bridgedMethod);
188 }
189
190 // look at interface methods too
191 for (MethodImplementationsTreeNode implementationsNode : treeBuilder.buildMethodImplementations(VoidTranslator.INSTANCE, methodEntry)) {
192 resolveEquivalentMethods(methodEntries, implementationsNode);
193 }
194
195 // recurse
196 for (int i = 0; i < node.getChildCount(); i++) {
197 resolveEquivalentMethods(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
198 }
199 }
200
201 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
202 MethodEntry methodEntry = node.getMethodEntry();
203 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
204 if (flags != null && !flags.isPrivate() && !flags.isStatic()) {
205 // collect the entry
206 methodEntries.add(methodEntry);
207 }
208
209 // look at bridge methods!
210 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromAccessed(methodEntry);
211 while (bridgedMethod != null) {
212 methodEntries.addAll(resolveEquivalentMethods(bridgedMethod));
213 bridgedMethod = bridgeMethodIndex.getBridgeFromAccessed(bridgedMethod);
214 }
215
216 // recurse
217 for (int i = 0; i < node.getChildCount(); i++) {
218 resolveEquivalentMethods(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
219 }
220 }
221
222 private boolean canInherit(MethodEntry entry, AccessFlags access) {
223 return !entry.isConstructor() && !access.isPrivate() && !access.isStatic() && !access.isFinal();
224 }
225}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
new file mode 100644
index 00000000..4fba49d5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
@@ -0,0 +1,56 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.Translatable;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
6import cuchaz.enigma.translation.mapping.tree.EntryTree;
7import cuchaz.enigma.translation.representation.entry.Entry;
8
9public class MappingDelta implements Translatable {
10 public static final Object PLACEHOLDER = new Object();
11
12 private final EntryTree<Object> additions;
13 private final EntryTree<Object> deletions;
14
15 public MappingDelta(EntryTree<Object> additions, EntryTree<Object> deletions) {
16 this.additions = additions;
17 this.deletions = deletions;
18 }
19
20 public MappingDelta() {
21 this(new HashEntryTree<>(), new HashEntryTree<>());
22 }
23
24 public static MappingDelta added(EntryTree<EntryMapping> mappings) {
25 EntryTree<Object> additions = new HashEntryTree<>();
26 for (Entry<?> entry : mappings.getAllEntries()) {
27 additions.insert(entry, PLACEHOLDER);
28 }
29
30 return new MappingDelta(additions, new HashEntryTree<>());
31 }
32
33 public EntryTree<?> getAdditions() {
34 return additions;
35 }
36
37 public EntryTree<?> getDeletions() {
38 return deletions;
39 }
40
41 @Override
42 public MappingDelta translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
43 return new MappingDelta(
44 translate(translator, additions),
45 translate(translator, deletions)
46 );
47 }
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}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
new file mode 100644
index 00000000..9ed7e8a5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
@@ -0,0 +1,28 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6
7public class MappingPair<E extends Entry<?>, M> {
8 private final E entry;
9 private final M mapping;
10
11 public MappingPair(E entry, @Nullable M mapping) {
12 this.entry = entry;
13 this.mapping = mapping;
14 }
15
16 public MappingPair(E entry) {
17 this(entry, null);
18 }
19
20 public E getEntry() {
21 return entry;
22 }
23
24 @Nullable
25 public M getMapping() {
26 return mapping;
27 }
28}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
new file mode 100644
index 00000000..422bf380
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
@@ -0,0 +1,45 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.throwables.IllegalNameException;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.translation.representation.entry.Entry;
7
8import java.util.Collection;
9
10public class MappingValidator {
11 private final EntryTree<EntryMapping> deobfToObf;
12 private final Translator deobfuscator;
13 private final EntryResolver entryResolver;
14
15 public MappingValidator(EntryTree<EntryMapping> deobfToObf, Translator deobfuscator, EntryResolver entryResolver) {
16 this.deobfToObf = deobfToObf;
17 this.deobfuscator = deobfuscator;
18 this.entryResolver = entryResolver;
19 }
20
21 public void validateRename(Entry<?> entry, String name) throws IllegalNameException {
22 Collection<Entry<?>> equivalentEntries = entryResolver.resolveEquivalentEntries(entry);
23 for (Entry<?> equivalentEntry : equivalentEntries) {
24 equivalentEntry.validateName(name);
25 validateUnique(equivalentEntry, name);
26 }
27 }
28
29 private void validateUnique(Entry<?> entry, String name) {
30 Entry<?> translatedEntry = deobfuscator.translate(entry);
31 Collection<Entry<?>> siblings = deobfToObf.getSiblings(translatedEntry);
32 if (!isUnique(translatedEntry, siblings, name)) {
33 throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!");
34 }
35 }
36
37 private boolean isUnique(Entry<?> entry, Collection<Entry<?>> siblings, String name) {
38 for (Entry<?> child : siblings) {
39 if (entry.canConflictWith(child) && child.getName().equals(name)) {
40 return false;
41 }
42 }
43 return true;
44 }
45}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
new file mode 100644
index 00000000..77d75ecb
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
@@ -0,0 +1,91 @@
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.mapping;
13
14import cuchaz.enigma.analysis.index.JarIndex;
15import cuchaz.enigma.translation.mapping.tree.EntryTree;
16import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import cuchaz.enigma.translation.representation.entry.Entry;
19import cuchaz.enigma.translation.representation.entry.FieldEntry;
20import cuchaz.enigma.translation.representation.entry.MethodEntry;
21
22import java.util.Collection;
23import java.util.HashMap;
24import java.util.Map;
25
26public class MappingsChecker {
27 private final JarIndex index;
28 private final EntryTree<EntryMapping> mappings;
29
30 public MappingsChecker(JarIndex index, EntryTree<EntryMapping> mappings) {
31 this.index = index;
32 this.mappings = mappings;
33 }
34
35 public Dropped dropBrokenMappings() {
36 Dropped dropped = new Dropped();
37
38 Collection<Entry<?>> obfEntries = mappings.getAllEntries();
39 for (Entry<?> entry : obfEntries) {
40 if (entry instanceof ClassEntry || entry instanceof MethodEntry || entry instanceof FieldEntry) {
41 tryDropEntry(dropped, entry);
42 }
43 }
44
45 dropped.apply(mappings);
46
47 return dropped;
48 }
49
50 private void tryDropEntry(Dropped dropped, Entry<?> entry) {
51 if (shouldDropEntry(entry)) {
52 EntryMapping mapping = mappings.get(entry);
53 if (mapping != null) {
54 dropped.drop(entry, mapping);
55 }
56 }
57 }
58
59 private boolean shouldDropEntry(Entry<?> entry) {
60 if (!index.getEntryIndex().hasEntry(entry)) {
61 return true;
62 }
63 Collection<Entry<?>> resolvedEntries = index.getEntryResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT);
64 return !resolvedEntries.contains(entry);
65 }
66
67 public static class Dropped {
68 private final Map<Entry<?>, String> droppedMappings = new HashMap<>();
69
70 public void drop(Entry<?> entry, EntryMapping mapping) {
71 droppedMappings.put(entry, mapping.getTargetName());
72 }
73
74 void apply(EntryTree<EntryMapping> mappings) {
75 for (Entry<?> entry : droppedMappings.keySet()) {
76 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
77 if (node == null) {
78 continue;
79 }
80
81 for (Entry<?> childEntry : node.getChildrenRecursively()) {
82 mappings.remove(childEntry);
83 }
84 }
85 }
86
87 public Map<Entry<?>, String> getDroppedMappings() {
88 return droppedMappings;
89 }
90 }
91}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java
index fca8cfcb..19473ead 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java
@@ -9,20 +9,19 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.translation.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.throwables.IllegalNameException; 14import cuchaz.enigma.throwables.IllegalNameException;
15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16 16
17import java.util.Arrays; 17import java.util.Arrays;
18import java.util.List; 18import java.util.List;
19import java.util.regex.Pattern; 19import java.util.regex.Pattern;
20 20
21public class NameValidator { 21public class NameValidator {
22 22 private static final Pattern IDENTIFIER_PATTERN;
23 private static final Pattern IdentifierPattern; 23 private static final Pattern CLASS_PATTERN;
24 private static final Pattern ClassPattern; 24 private static final List<String> ILLEGAL_IDENTIFIERS = Arrays.asList(
25 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 25 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 26 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 27 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
@@ -32,42 +31,26 @@ public class NameValidator {
32 31
33 static { 32 static {
34 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; 33 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
35 IdentifierPattern = Pattern.compile(identifierRegex); 34 IDENTIFIER_PATTERN = Pattern.compile(identifierRegex);
36 ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); 35 CLASS_PATTERN = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex));
37 } 36 }
38 37
39 public static String validateClassName(String name, boolean packageRequired) { 38 public static void validateClassName(String name, boolean packageRequired) {
40 if (name == null) { 39 if (!CLASS_PATTERN.matcher(name).matches() || ILLEGAL_IDENTIFIERS.contains(name)) {
41 return null;
42 }
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 40 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 } 41 }
46 if (packageRequired && ClassEntry.getPackageName(name) == null) { 42 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package"); 43 throw new IllegalNameException(name, "Class must be in a package");
48 } 44 }
49 return name;
50 } 45 }
51 46
52 public static String validateFieldName(String name) { 47 public static void validateIdentifier(String name) {
53 if (name == null) { 48 if (!IDENTIFIER_PATTERN.matcher(name).matches() || ILLEGAL_IDENTIFIERS.contains(name)) {
54 return null;
55 }
56 if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) {
57 throw new IllegalNameException(name, "This doesn't look like a legal identifier"); 49 throw new IllegalNameException(name, "This doesn't look like a legal identifier");
58 } 50 }
59 return name;
60 }
61
62 public static String validateMethodName(String name) {
63 return validateFieldName(name);
64 }
65
66 public static String validateArgumentName(String name) {
67 return validateFieldName(name);
68 } 51 }
69 52
70 public static boolean isReserved(String name) { 53 public static boolean isReserved(String name) {
71 return ReservedWords.contains(name); 54 return ILLEGAL_IDENTIFIERS.contains(name);
72 } 55 }
73} 56}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java b/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java
new file mode 100644
index 00000000..1c28e028
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java
@@ -0,0 +1,6 @@
1package cuchaz.enigma.translation.mapping;
2
3public enum ResolutionStrategy {
4 RESOLVE_ROOT,
5 RESOLVE_CLOSEST
6}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
new file mode 100644
index 00000000..2eab55fd
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
@@ -0,0 +1,27 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4import cuchaz.enigma.translation.representation.entry.MethodEntry;
5
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Set;
9
10public enum VoidEntryResolver implements EntryResolver {
11 INSTANCE;
12
13 @Override
14 public <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy) {
15 return Collections.singleton(entry);
16 }
17
18 @Override
19 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) {
20 return Collections.singleton(entry);
21 }
22
23 @Override
24 public Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry) {
25 return Collections.singleton(methodEntry);
26 }
27}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java
new file mode 100644
index 00000000..d36bc0bc
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java
@@ -0,0 +1,260 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.AccessModifier;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingPair;
8import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
9import cuchaz.enigma.translation.mapping.tree.EntryTree;
10import cuchaz.enigma.translation.representation.MethodDescriptor;
11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.*;
13
14import javax.annotation.Nullable;
15import java.io.IOException;
16import java.nio.file.Files;
17import java.nio.file.Path;
18import java.util.ArrayDeque;
19import java.util.Deque;
20import java.util.List;
21import java.util.Locale;
22import java.util.stream.Collectors;
23
24public enum EnigmaMappingsReader implements MappingsReader {
25 FILE {
26 @Override
27 public EntryTree<EntryMapping> read(Path path) throws IOException, MappingParseException {
28 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
29 readFile(path, mappings);
30 return mappings;
31 }
32 },
33 DIRECTORY {
34 @Override
35 public EntryTree<EntryMapping> read(Path path) throws IOException, MappingParseException {
36 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
37
38 List<Path> files = Files.walk(path)
39 .filter(f -> !Files.isDirectory(f))
40 .filter(f -> f.toString().endsWith(".mapping"))
41 .collect(Collectors.toList());
42 for (Path file : files) {
43 if (Files.isHidden(file)) {
44 continue;
45 }
46 readFile(file, mappings);
47 }
48
49 return mappings;
50 }
51 };
52
53 protected void readFile(Path path, EntryTree<EntryMapping> mappings) throws IOException, MappingParseException {
54 List<String> lines = Files.readAllLines(path, Charsets.UTF_8);
55 Deque<Entry<?>> mappingStack = new ArrayDeque<>();
56
57 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
58 String line = lines.get(lineNumber);
59 int indentation = countIndentation(line);
60
61 line = formatLine(line);
62 if (line == null) {
63 continue;
64 }
65
66 while (indentation < mappingStack.size()) {
67 mappingStack.pop();
68 }
69
70 try {
71 MappingPair<?, EntryMapping> pair = parseLine(mappingStack.peek(), line);
72 mappingStack.push(pair.getEntry());
73 if (pair.getMapping() != null) {
74 mappings.insert(pair.getEntry(), pair.getMapping());
75 }
76 } catch (Throwable t) {
77 t.printStackTrace();
78 throw new MappingParseException(path::toString, lineNumber, t.toString());
79 }
80 }
81 }
82
83 @Nullable
84 private String formatLine(String line) {
85 line = stripComment(line);
86 line = line.trim();
87
88 if (line.isEmpty()) {
89 return null;
90 }
91
92 return line;
93 }
94
95 private String stripComment(String line) {
96 int commentPos = line.indexOf('#');
97 if (commentPos >= 0) {
98 return line.substring(0, commentPos);
99 }
100 return line;
101 }
102
103 private int countIndentation(String line) {
104 int indent = 0;
105 for (int i = 0; i < line.length(); i++) {
106 if (line.charAt(i) != '\t') {
107 break;
108 }
109 indent++;
110 }
111 return indent;
112 }
113
114 private MappingPair<?, EntryMapping> parseLine(@Nullable Entry<?> parent, String line) {
115 String[] tokens = line.trim().split("\\s");
116 String keyToken = tokens[0].toLowerCase(Locale.ROOT);
117
118 switch (keyToken) {
119 case "class":
120 return parseClass(parent, tokens);
121 case "field":
122 return parseField(parent, tokens);
123 case "method":
124 return parseMethod(parent, tokens);
125 case "arg":
126 return parseArgument(parent, tokens);
127 default:
128 throw new RuntimeException("Unknown token '" + keyToken + "'");
129 }
130 }
131
132 private MappingPair<ClassEntry, EntryMapping> parseClass(@Nullable Entry<?> parent, String[] tokens) {
133 String obfuscatedName = ClassEntry.getInnerName(tokens[1]);
134 ClassEntry obfuscatedEntry;
135 if (parent instanceof ClassEntry) {
136 obfuscatedEntry = new ClassEntry((ClassEntry) parent, obfuscatedName);
137 } else {
138 obfuscatedEntry = new ClassEntry(obfuscatedName);
139 }
140
141 String mapping = null;
142 AccessModifier modifier = AccessModifier.UNCHANGED;
143
144 if (tokens.length == 3) {
145 AccessModifier parsedModifier = parseModifier(tokens[2]);
146 if (parsedModifier != null) {
147 modifier = parsedModifier;
148 mapping = obfuscatedName;
149 } else {
150 mapping = tokens[2];
151 }
152 } else if (tokens.length == 4) {
153 mapping = tokens[2];
154 modifier = parseModifier(tokens[3]);
155 }
156
157 if (mapping != null) {
158 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping, modifier));
159 } else {
160 return new MappingPair<>(obfuscatedEntry);
161 }
162 }
163
164 private MappingPair<FieldEntry, EntryMapping> parseField(@Nullable Entry<?> parent, String[] tokens) {
165 if (!(parent instanceof ClassEntry)) {
166 throw new RuntimeException("Field must be a child of a class!");
167 }
168
169 ClassEntry ownerEntry = (ClassEntry) parent;
170
171 String obfuscatedName = tokens[1];
172 String mapping = obfuscatedName;
173 AccessModifier modifier = AccessModifier.UNCHANGED;
174 TypeDescriptor descriptor;
175
176 if (tokens.length == 4) {
177 AccessModifier parsedModifier = parseModifier(tokens[3]);
178 if (parsedModifier != null) {
179 descriptor = new TypeDescriptor(tokens[2]);
180 modifier = parsedModifier;
181 } else {
182 mapping = tokens[2];
183 descriptor = new TypeDescriptor(tokens[3]);
184 }
185 } else if (tokens.length == 5) {
186 descriptor = new TypeDescriptor(tokens[3]);
187 mapping = tokens[2];
188 modifier = parseModifier(tokens[4]);
189 } else {
190 throw new RuntimeException("Invalid method declaration");
191 }
192
193 FieldEntry obfuscatedEntry = new FieldEntry(ownerEntry, obfuscatedName, descriptor);
194 if (mapping != null) {
195 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping, modifier));
196 } else {
197 return new MappingPair<>(obfuscatedEntry);
198 }
199 }
200
201 private MappingPair<MethodEntry, EntryMapping> parseMethod(@Nullable Entry<?> parent, String[] tokens) {
202 if (!(parent instanceof ClassEntry)) {
203 throw new RuntimeException("Method must be a child of a class!");
204 }
205
206 ClassEntry ownerEntry = (ClassEntry) parent;
207
208 String obfuscatedName = tokens[1];
209 String mapping = null;
210 AccessModifier modifier = AccessModifier.UNCHANGED;
211 MethodDescriptor descriptor;
212
213 if (tokens.length == 3) {
214 descriptor = new MethodDescriptor(tokens[2]);
215 } else if (tokens.length == 4) {
216 AccessModifier parsedModifier = parseModifier(tokens[3]);
217 if (parsedModifier != null) {
218 modifier = parsedModifier;
219 mapping = obfuscatedName;
220 descriptor = new MethodDescriptor(tokens[2]);
221 } else {
222 mapping = tokens[2];
223 descriptor = new MethodDescriptor(tokens[3]);
224 }
225 } else if (tokens.length == 5) {
226 mapping = tokens[2];
227 modifier = parseModifier(tokens[4]);
228 descriptor = new MethodDescriptor(tokens[3]);
229 } else {
230 throw new RuntimeException("Invalid method declaration");
231 }
232
233 MethodEntry obfuscatedEntry = new MethodEntry(ownerEntry, obfuscatedName, descriptor);
234 if (mapping != null) {
235 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping, modifier));
236 } else {
237 return new MappingPair<>(obfuscatedEntry);
238 }
239 }
240
241 private MappingPair<LocalVariableEntry, EntryMapping> parseArgument(@Nullable Entry<?> parent, String[] tokens) {
242 if (!(parent instanceof MethodEntry)) {
243 throw new RuntimeException("Method arg must be a child of a method!");
244 }
245
246 MethodEntry ownerEntry = (MethodEntry) parent;
247 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerEntry, Integer.parseInt(tokens[1]), "", true);
248 String mapping = tokens[2];
249
250 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
251 }
252
253 @Nullable
254 private AccessModifier parseModifier(String token) {
255 if (token.startsWith("ACC:")) {
256 return AccessModifier.valueOf(token.substring(4));
257 }
258 return null;
259 }
260}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
new file mode 100644
index 00000000..3eef7390
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
@@ -0,0 +1,260 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.mapping.serde;
13
14import cuchaz.enigma.ProgressListener;
15import cuchaz.enigma.translation.MappingTranslator;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.AccessModifier;
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;
22import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
23import cuchaz.enigma.translation.representation.entry.*;
24
25import java.io.IOException;
26import java.io.PrintWriter;
27import java.nio.file.DirectoryStream;
28import java.nio.file.Files;
29import java.nio.file.Path;
30import java.nio.file.Paths;
31import java.util.ArrayList;
32import java.util.Collection;
33import java.util.concurrent.atomic.AtomicInteger;
34import java.util.stream.Collectors;
35
36public enum EnigmaMappingsWriter implements MappingsWriter {
37 FILE {
38 @Override
39 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) {
40 Collection<ClassEntry> classes = mappings.getRootEntries().stream()
41 .filter(entry -> entry instanceof ClassEntry)
42 .map(entry -> (ClassEntry) entry)
43 .collect(Collectors.toList());
44
45 progress.init(classes.size(), "Writing classes");
46
47 int steps = 0;
48 try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(path))) {
49 for (ClassEntry classEntry : classes) {
50 progress.step(steps++, classEntry.getFullName());
51 writeRoot(writer, mappings, classEntry);
52 }
53 } catch (IOException e) {
54 e.printStackTrace();
55 }
56 }
57 },
58 DIRECTORY {
59 @Override
60 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) {
61 applyDeletions(delta.getDeletions(), path);
62
63 Collection<ClassEntry> classes = delta.getAdditions().getRootEntries().stream()
64 .filter(entry -> entry instanceof ClassEntry)
65 .map(entry -> (ClassEntry) entry)
66 .collect(Collectors.toList());
67
68 progress.init(classes.size(), "Writing classes");
69
70 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
71 AtomicInteger steps = new AtomicInteger();
72
73 classes.parallelStream().forEach(classEntry -> {
74 progress.step(steps.getAndIncrement(), classEntry.getFullName());
75
76 try {
77 Path classPath = resolve(path, translator.translate(classEntry));
78 Files.deleteIfExists(classPath);
79 Files.createDirectories(classPath.getParent());
80
81 try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(classPath))) {
82 writeRoot(writer, mappings, classEntry);
83 }
84 } catch (Throwable t) {
85 System.err.println("Failed to write class '" + classEntry.getFullName() + "'");
86 t.printStackTrace();
87 }
88 });
89 }
90
91 private void applyDeletions(EntryTree<?> deletions, Path root) {
92 Collection<ClassEntry> deletedClasses = deletions.getRootEntries().stream()
93 .filter(e -> e instanceof ClassEntry)
94 .map(e -> (ClassEntry) e)
95 .collect(Collectors.toList());
96
97 for (ClassEntry classEntry : deletedClasses) {
98 try {
99 Files.deleteIfExists(resolve(root, classEntry));
100 } catch (IOException e) {
101 System.err.println("Failed to delete deleted class '" + classEntry + "'");
102 e.printStackTrace();
103 }
104 }
105
106 for (ClassEntry classEntry : deletedClasses) {
107 String packageName = classEntry.getPackageName();
108 if (packageName != null) {
109 Path packagePath = Paths.get(packageName);
110 try {
111 deleteDeadPackages(root, packagePath);
112 } catch (IOException e) {
113 System.err.println("Failed to delete dead package '" + packageName + "'");
114 e.printStackTrace();
115 }
116 }
117 }
118 }
119
120 private void deleteDeadPackages(Path root, Path packagePath) throws IOException {
121 for (int i = packagePath.getNameCount() - 1; i >= 0; i--) {
122 Path subPath = packagePath.subpath(0, i + 1);
123 Path packagePart = root.resolve(subPath);
124 if (isEmpty(packagePart)) {
125 Files.deleteIfExists(packagePart);
126 }
127 }
128 }
129
130 private boolean isEmpty(Path path) {
131 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
132 return !stream.iterator().hasNext();
133 } catch (IOException e) {
134 return false;
135 }
136 }
137
138 private Path resolve(Path root, ClassEntry classEntry) {
139 return root.resolve(classEntry.getFullName() + ".mapping");
140 }
141 };
142
143 protected void writeRoot(PrintWriter writer, EntryTree<EntryMapping> mappings, ClassEntry classEntry) {
144 Collection<Entry<?>> children = groupChildren(mappings.getChildren(classEntry));
145
146 writer.println(writeClass(classEntry, mappings.get(classEntry)).trim());
147 for (Entry<?> child : children) {
148 writeEntry(writer, mappings, child, 1);
149 }
150 }
151
152 protected void writeEntry(PrintWriter writer, EntryTree<EntryMapping> mappings, Entry<?> entry, int depth) {
153 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
154 if (node == null) {
155 return;
156 }
157
158 EntryMapping mapping = node.getValue();
159 if (entry instanceof ClassEntry) {
160 String line = writeClass((ClassEntry) entry, mapping);
161 writer.println(indent(line, depth));
162 } else if (entry instanceof MethodEntry) {
163 String line = writeMethod((MethodEntry) entry, mapping);
164 writer.println(indent(line, depth));
165 } else if (entry instanceof FieldEntry) {
166 String line = writeField((FieldEntry) entry, mapping);
167 writer.println(indent(line, depth));
168 } else if (entry instanceof LocalVariableEntry) {
169 String line = writeArgument((LocalVariableEntry) entry, mapping);
170 writer.println(indent(line, depth));
171 }
172
173 Collection<Entry<?>> children = groupChildren(node.getChildren());
174 for (Entry<?> child : children) {
175 writeEntry(writer, mappings, child, depth + 1);
176 }
177 }
178
179 private Collection<Entry<?>> groupChildren(Collection<Entry<?>> children) {
180 Collection<Entry<?>> result = new ArrayList<>(children.size());
181
182 children.stream().filter(e -> e instanceof ClassEntry)
183 .map(e -> (ClassEntry) e)
184 .sorted()
185 .forEach(result::add);
186
187 children.stream().filter(e -> e instanceof FieldEntry)
188 .map(e -> (FieldEntry) e)
189 .sorted()
190 .forEach(result::add);
191
192 children.stream().filter(e -> e instanceof MethodEntry)
193 .map(e -> (MethodEntry) e)
194 .sorted()
195 .forEach(result::add);
196
197 children.stream().filter(e -> e instanceof LocalVariableEntry)
198 .map(e -> (LocalVariableEntry) e)
199 .sorted()
200 .forEach(result::add);
201
202 return result;
203 }
204
205 protected String writeClass(ClassEntry entry, EntryMapping mapping) {
206 StringBuilder builder = new StringBuilder("CLASS ");
207 builder.append(entry.getFullName()).append(' ');
208 writeMapping(builder, mapping);
209
210 return builder.toString();
211 }
212
213 protected String writeMethod(MethodEntry entry, EntryMapping mapping) {
214 StringBuilder builder = new StringBuilder("METHOD ");
215 builder.append(entry.getName()).append(' ');
216 writeMapping(builder, mapping);
217
218 builder.append(entry.getDesc().toString());
219
220 return builder.toString();
221 }
222
223 protected String writeField(FieldEntry entry, EntryMapping mapping) {
224 StringBuilder builder = new StringBuilder("FIELD ");
225 builder.append(entry.getName()).append(' ');
226 writeMapping(builder, mapping);
227
228 builder.append(entry.getDesc().toString());
229
230 return builder.toString();
231 }
232
233 protected String writeArgument(LocalVariableEntry entry, EntryMapping mapping) {
234 StringBuilder builder = new StringBuilder("ARG ");
235 builder.append(entry.getIndex()).append(' ');
236
237 String mappedName = mapping != null ? mapping.getTargetName() : entry.getName();
238 builder.append(mappedName);
239
240 return builder.toString();
241 }
242
243 private void writeMapping(StringBuilder builder, EntryMapping mapping) {
244 if (mapping != null) {
245 builder.append(mapping.getTargetName()).append(' ');
246 if (mapping.getAccessModifier() != AccessModifier.UNCHANGED) {
247 builder.append(mapping.getAccessModifier().getFormattedName()).append(' ');
248 }
249 }
250 }
251
252 private String indent(String line, int depth) {
253 StringBuilder builder = new StringBuilder();
254 for (int i = 0; i < depth; i++) {
255 builder.append("\t");
256 }
257 builder.append(line.trim());
258 return builder.toString();
259 }
260}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
new file mode 100644
index 00000000..4db16454
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
@@ -0,0 +1,54 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingDelta;
7import cuchaz.enigma.translation.mapping.tree.EntryTree;
8
9import javax.annotation.Nullable;
10import java.io.IOException;
11import java.nio.file.Path;
12
13public enum MappingFormat {
14 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE),
15 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY),
16 TINY_FILE(null, TinyMappingsReader.INSTANCE),
17 SRG_FILE(SrgMappingsWriter.INSTANCE, null);
18
19 private final MappingsWriter writer;
20 private final MappingsReader reader;
21
22 MappingFormat(MappingsWriter writer, MappingsReader reader) {
23 this.writer = writer;
24 this.reader = reader;
25 }
26
27 public void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progressListener) {
28 write(mappings, MappingDelta.added(mappings), path, progressListener);
29 }
30
31 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progressListener) {
32 if (writer == null) {
33 throw new IllegalStateException(name() + " does not support writing");
34 }
35 writer.write(mappings, delta, path, progressListener);
36 }
37
38 public EntryTree<EntryMapping> read(Path path) throws IOException, MappingParseException {
39 if (reader == null) {
40 throw new IllegalStateException(name() + " does not support reading");
41 }
42 return reader.read(path);
43 }
44
45 @Nullable
46 public MappingsWriter getWriter() {
47 return writer;
48 }
49
50 @Nullable
51 public MappingsReader getReader() {
52 return reader;
53 }
54}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
new file mode 100644
index 00000000..f239ee67
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
@@ -0,0 +1,12 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.throwables.MappingParseException;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6
7import java.io.IOException;
8import java.nio.file.Path;
9
10public interface MappingsReader {
11 EntryTree<EntryMapping> read(Path path) throws MappingParseException, IOException;
12}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
new file mode 100644
index 00000000..b5196681
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
@@ -0,0 +1,12 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.MappingDelta;
6import cuchaz.enigma.translation.mapping.tree.EntryTree;
7
8import java.nio.file.Path;
9
10public interface MappingsWriter {
11 void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress);
12}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
new file mode 100644
index 00000000..15ba4d75
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
@@ -0,0 +1,115 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.collect.Lists;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.MappingTranslator;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingDelta;
9import cuchaz.enigma.translation.mapping.VoidEntryResolver;
10import cuchaz.enigma.translation.mapping.tree.EntryTree;
11import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
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.MethodEntry;
16
17import java.io.IOException;
18import java.io.PrintWriter;
19import java.nio.file.Files;
20import java.nio.file.Path;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Comparator;
24import java.util.List;
25import java.util.stream.Collectors;
26
27public enum SrgMappingsWriter implements MappingsWriter {
28 INSTANCE;
29
30 @Override
31 public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) {
32 try {
33 Files.deleteIfExists(path);
34 Files.createFile(path);
35 } catch (IOException e) {
36 e.printStackTrace();
37 }
38
39 List<String> classLines = new ArrayList<>();
40 List<String> fieldLines = new ArrayList<>();
41 List<String> methodLines = new ArrayList<>();
42
43 Collection<Entry<?>> rootEntries = Lists.newArrayList(mappings).stream()
44 .map(EntryTreeNode::getEntry)
45 .collect(Collectors.toList());
46 progress.init(rootEntries.size(), "Generating mappings");
47
48 int steps = 0;
49 for (Entry<?> entry : sorted(rootEntries)) {
50 progress.step(steps++, entry.getName());
51 writeEntry(classLines, fieldLines, methodLines, mappings, entry);
52 }
53
54 progress.init(3, "Writing mappings");
55 try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(path))) {
56 progress.step(0, "Classes");
57 classLines.forEach(writer::println);
58 progress.step(1, "Fields");
59 fieldLines.forEach(writer::println);
60 progress.step(2, "Methods");
61 methodLines.forEach(writer::println);
62 } catch (IOException e) {
63 e.printStackTrace();
64 }
65 }
66
67 private void writeEntry(List<String> classes, List<String> fields, List<String> methods, EntryTree<EntryMapping> mappings, Entry<?> entry) {
68 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
69 if (node == null) {
70 return;
71 }
72
73 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
74 if (entry instanceof ClassEntry) {
75 classes.add(generateClassLine((ClassEntry) entry, translator));
76 } else if (entry instanceof FieldEntry) {
77 fields.add(generateFieldLine((FieldEntry) entry, translator));
78 } else if (entry instanceof MethodEntry) {
79 methods.add(generateMethodLine((MethodEntry) entry, translator));
80 }
81
82 for (Entry<?> child : sorted(node.getChildren())) {
83 writeEntry(classes, fields, methods, mappings, child);
84 }
85 }
86
87 private String generateClassLine(ClassEntry sourceEntry, Translator translator) {
88 ClassEntry targetEntry = translator.translate(sourceEntry);
89 return "CL: " + sourceEntry.getFullName() + " " + targetEntry.getFullName();
90 }
91
92 private String generateMethodLine(MethodEntry sourceEntry, Translator translator) {
93 MethodEntry targetEntry = translator.translate(sourceEntry);
94 return "MD: " + describeMethod(sourceEntry) + " " + describeMethod(targetEntry);
95 }
96
97 private String describeMethod(MethodEntry entry) {
98 return entry.getParent().getFullName() + "/" + entry.getName() + " " + entry.getDesc();
99 }
100
101 private String generateFieldLine(FieldEntry sourceEntry, Translator translator) {
102 FieldEntry targetEntry = translator.translate(sourceEntry);
103 return "FD: " + describeField(sourceEntry) + " " + describeField(targetEntry);
104 }
105
106 private String describeField(FieldEntry entry) {
107 return entry.getParent().getFullName() + "/" + entry.getName();
108 }
109
110 private Collection<Entry<?>> sorted(Iterable<Entry<?>> iterable) {
111 ArrayList<Entry<?>> sorted = Lists.newArrayList(iterable);
112 sorted.sort(Comparator.comparing(Entry::getName));
113 return sorted;
114 }
115}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java
new file mode 100644
index 00000000..e0afc3e8
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java
@@ -0,0 +1,100 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingPair;
7import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.representation.MethodDescriptor;
10import cuchaz.enigma.translation.representation.TypeDescriptor;
11import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import cuchaz.enigma.translation.representation.entry.FieldEntry;
13import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15
16import java.io.IOException;
17import java.nio.file.Files;
18import java.nio.file.Path;
19import java.util.List;
20
21public enum TinyMappingsReader implements MappingsReader {
22 INSTANCE;
23
24 @Override
25 public EntryTree<EntryMapping> read(Path path) throws IOException, MappingParseException {
26 return read(path, Files.readAllLines(path, Charsets.UTF_8));
27 }
28
29 private EntryTree<EntryMapping> read(Path path, List<String> lines) throws MappingParseException {
30 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
31 lines.remove(0);
32
33 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
34 String line = lines.get(lineNumber);
35
36 try {
37 MappingPair<?, EntryMapping> mapping = parseLine(line);
38 mappings.insert(mapping.getEntry(), mapping.getMapping());
39 } catch (Throwable t) {
40 t.printStackTrace();
41 throw new MappingParseException(path::toString, lineNumber, t.toString());
42 }
43 }
44
45 return mappings;
46 }
47
48 private MappingPair<?, EntryMapping> parseLine(String line) {
49 String[] tokens = line.split("\t");
50
51 String key = tokens[0];
52 switch (key) {
53 case "CLASS":
54 return parseClass(tokens);
55 case "FIELD":
56 return parseField(tokens);
57 case "METHOD":
58 return parseMethod(tokens);
59 case "MTH-ARG":
60 return parseArgument(tokens);
61 default:
62 throw new RuntimeException("Unknown token '" + key + "'!");
63 }
64 }
65
66 private MappingPair<ClassEntry, EntryMapping> parseClass(String[] tokens) {
67 ClassEntry obfuscatedEntry = new ClassEntry(tokens[1]);
68 String mapping = tokens[2];
69 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
70 }
71
72 private MappingPair<FieldEntry, EntryMapping> parseField(String[] tokens) {
73 ClassEntry ownerClass = new ClassEntry(tokens[1]);
74 TypeDescriptor descriptor = new TypeDescriptor(tokens[2]);
75
76 FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, tokens[3], descriptor);
77 String mapping = tokens[4];
78 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
79 }
80
81 private MappingPair<MethodEntry, EntryMapping> parseMethod(String[] tokens) {
82 ClassEntry ownerClass = new ClassEntry(tokens[1]);
83 MethodDescriptor descriptor = new MethodDescriptor(tokens[2]);
84
85 MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, tokens[3], descriptor);
86 String mapping = tokens[4];
87 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
88 }
89
90 private MappingPair<LocalVariableEntry, EntryMapping> parseArgument(String[] tokens) {
91 ClassEntry ownerClass = new ClassEntry(tokens[1]);
92 MethodDescriptor ownerDescriptor = new MethodDescriptor(tokens[2]);
93 MethodEntry ownerMethod = new MethodEntry(ownerClass, tokens[3], ownerDescriptor);
94 int variableIndex = Integer.parseInt(tokens[4]);
95
96 String mapping = tokens[5];
97 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true);
98 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
99 }
100}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
new file mode 100644
index 00000000..98a01df2
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
@@ -0,0 +1,113 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.mapping.MappingDelta;
4import cuchaz.enigma.translation.representation.entry.Entry;
5
6import javax.annotation.Nullable;
7import java.util.Collection;
8import java.util.Iterator;
9
10public class DeltaTrackingTree<T> implements EntryTree<T> {
11 private final EntryTree<T> delegate;
12
13 private EntryTree<Object> additions = new HashEntryTree<>();
14 private EntryTree<Object> deletions = new HashEntryTree<>();
15
16 public DeltaTrackingTree(EntryTree<T> delegate) {
17 this.delegate = delegate;
18 }
19
20 public DeltaTrackingTree() {
21 this(new HashEntryTree<>());
22 }
23
24 @Override
25 public void insert(Entry<?> entry, T value) {
26 if (value != null) {
27 trackAddition(entry);
28 } else {
29 trackDeletion(entry);
30 }
31 delegate.insert(entry, value);
32 }
33
34 @Nullable
35 @Override
36 public T remove(Entry<?> entry) {
37 T value = delegate.remove(entry);
38 trackDeletion(entry);
39 return value;
40 }
41
42 public void trackAddition(Entry<?> entry) {
43 deletions.remove(entry);
44 additions.insert(entry, MappingDelta.PLACEHOLDER);
45 }
46
47 public void trackDeletion(Entry<?> entry) {
48 additions.remove(entry);
49 deletions.insert(entry, MappingDelta.PLACEHOLDER);
50 }
51
52 @Nullable
53 @Override
54 public T get(Entry<?> entry) {
55 return delegate.get(entry);
56 }
57
58 @Override
59 public Collection<Entry<?>> getChildren(Entry<?> entry) {
60 return delegate.getChildren(entry);
61 }
62
63 @Override
64 public Collection<Entry<?>> getSiblings(Entry<?> entry) {
65 return delegate.getSiblings(entry);
66 }
67
68 @Nullable
69 @Override
70 public EntryTreeNode<T> findNode(Entry<?> entry) {
71 return delegate.findNode(entry);
72 }
73
74 @Override
75 public Collection<EntryTreeNode<T>> getAllNodes() {
76 return delegate.getAllNodes();
77 }
78
79 @Override
80 public Collection<Entry<?>> getRootEntries() {
81 return delegate.getRootEntries();
82 }
83
84 @Override
85 public Collection<Entry<?>> getAllEntries() {
86 return delegate.getAllEntries();
87 }
88
89 @Override
90 public boolean isEmpty() {
91 return delegate.isEmpty();
92 }
93
94 @Override
95 public Iterator<EntryTreeNode<T>> iterator() {
96 return delegate.iterator();
97 }
98
99 public MappingDelta takeDelta() {
100 MappingDelta delta = new MappingDelta(additions, deletions);
101 resetDelta();
102 return delta;
103 }
104
105 private void resetDelta() {
106 additions = new HashEntryTree<>();
107 deletions = new HashEntryTree<>();
108 }
109
110 public boolean isDirty() {
111 return !additions.isEmpty() || !deletions.isEmpty();
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
new file mode 100644
index 00000000..73fe12d0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
@@ -0,0 +1,20 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.mapping.EntryMap;
4import cuchaz.enigma.translation.representation.entry.Entry;
5
6import javax.annotation.Nullable;
7import java.util.Collection;
8
9public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>> {
10 Collection<Entry<?>> getChildren(Entry<?> entry);
11
12 Collection<Entry<?>> getSiblings(Entry<?> entry);
13
14 @Nullable
15 EntryTreeNode<T> findNode(Entry<?> entry);
16
17 Collection<EntryTreeNode<T>> getAllNodes();
18
19 Collection<Entry<?>> getRootEntries();
20}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
new file mode 100644
index 00000000..734b60ce
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
@@ -0,0 +1,36 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.stream.Collectors;
9
10public interface EntryTreeNode<T> {
11 @Nullable
12 T getValue();
13
14 Entry<?> getEntry();
15
16 boolean isEmpty();
17
18 Collection<Entry<?>> getChildren();
19
20 Collection<? extends EntryTreeNode<T>> getChildNodes();
21
22 default Collection<? extends EntryTreeNode<T>> getNodesRecursively() {
23 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
24 nodes.add(this);
25 for (EntryTreeNode<T> node : getChildNodes()) {
26 nodes.addAll(node.getNodesRecursively());
27 }
28 return nodes;
29 }
30
31 default Collection<Entry<?>> getChildrenRecursively() {
32 return getNodesRecursively().stream()
33 .map(EntryTreeNode::getEntry)
34 .collect(Collectors.toList());
35 }
36}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
new file mode 100644
index 00000000..ff88bf94
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
@@ -0,0 +1,159 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.*;
7import java.util.stream.Collectors;
8
9public class HashEntryTree<T> implements EntryTree<T> {
10 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>();
11
12 @Override
13 public void insert(Entry<?> entry, T value) {
14 List<HashTreeNode<T>> path = computePath(entry);
15 path.get(path.size() - 1).putValue(value);
16 if (value == null) {
17 removeDeadAlong(path);
18 }
19 }
20
21 @Override
22 @Nullable
23 public T remove(Entry<?> entry) {
24 List<HashTreeNode<T>> path = computePath(entry);
25 T value = path.get(path.size() - 1).removeValue();
26
27 removeDeadAlong(path);
28
29 return value;
30 }
31
32 @Override
33 @Nullable
34 public T get(Entry<?> entry) {
35 HashTreeNode<T> node = findNode(entry);
36 if (node == null) {
37 return null;
38 }
39 return node.getValue();
40 }
41
42 @Override
43 public boolean contains(Entry<?> entry) {
44 return get(entry) != null;
45 }
46
47 @Override
48 public Collection<Entry<?>> getChildren(Entry<?> entry) {
49 HashTreeNode<T> leaf = findNode(entry);
50 if (leaf == null) {
51 return Collections.emptyList();
52 }
53 return leaf.getChildren();
54 }
55
56 @Override
57 public Collection<Entry<?>> getSiblings(Entry<?> entry) {
58 List<HashTreeNode<T>> path = computePath(entry);
59 if (path.size() <= 1) {
60 return getSiblings(entry, root.keySet());
61 }
62 HashTreeNode<T> parent = path.get(path.size() - 2);
63 return getSiblings(entry, parent.getChildren());
64 }
65
66 private Collection<Entry<?>> getSiblings(Entry<?> entry, Collection<Entry<?>> children) {
67 Set<Entry<?>> siblings = new HashSet<>(children);
68 siblings.remove(entry);
69 return siblings;
70 }
71
72 @Override
73 @Nullable
74 public HashTreeNode<T> findNode(Entry<?> target) {
75 List<Entry<?>> parentChain = target.getAncestry();
76 if (parentChain.isEmpty()) {
77 return null;
78 }
79
80 HashTreeNode<T> node = root.get(parentChain.get(0));
81 for (int i = 1; i < parentChain.size(); i++) {
82 if (node == null) {
83 return null;
84 }
85 node = node.getChild(parentChain.get(i), false);
86 }
87
88 return node;
89 }
90
91 private List<HashTreeNode<T>> computePath(Entry<?> target) {
92 List<Entry<?>> ancestry = target.getAncestry();
93 if (ancestry.isEmpty()) {
94 return Collections.emptyList();
95 }
96
97 List<HashTreeNode<T>> path = new ArrayList<>(ancestry.size());
98
99 Entry<?> rootEntry = ancestry.get(0);
100 HashTreeNode<T> node = root.computeIfAbsent(rootEntry, HashTreeNode::new);
101 path.add(node);
102
103 for (int i = 1; i < ancestry.size(); i++) {
104 node = node.getChild(ancestry.get(i), true);
105 path.add(node);
106 }
107
108 return path;
109 }
110
111 private void removeDeadAlong(List<HashTreeNode<T>> path) {
112 for (int i = path.size() - 1; i >= 0; i--) {
113 HashTreeNode<T> node = path.get(i);
114 if (node.isEmpty()) {
115 if (i > 0) {
116 HashTreeNode<T> parentNode = path.get(i - 1);
117 parentNode.remove(node.getEntry());
118 } else {
119 root.remove(node.getEntry());
120 }
121 } else {
122 break;
123 }
124 }
125 }
126
127 @Override
128 @SuppressWarnings("unchecked")
129 public Iterator<EntryTreeNode<T>> iterator() {
130 Collection<EntryTreeNode<T>> values = (Collection) root.values();
131 return values.iterator();
132 }
133
134 @Override
135 public Collection<EntryTreeNode<T>> getAllNodes() {
136 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
137 for (EntryTreeNode<T> node : root.values()) {
138 nodes.addAll(node.getNodesRecursively());
139 }
140 return nodes;
141 }
142
143 @Override
144 public Collection<Entry<?>> getAllEntries() {
145 return getAllNodes().stream()
146 .map(EntryTreeNode::getEntry)
147 .collect(Collectors.toList());
148 }
149
150 @Override
151 public Collection<Entry<?>> getRootEntries() {
152 return root.keySet();
153 }
154
155 @Override
156 public boolean isEmpty() {
157 return root.isEmpty();
158 }
159}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
new file mode 100644
index 00000000..90e91647
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
@@ -0,0 +1,72 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.Collection;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.Map;
10
11public class HashTreeNode<T> implements EntryTreeNode<T>, Iterable<HashTreeNode<T>> {
12 private final Entry<?> entry;
13 private final Map<Entry<?>, HashTreeNode<T>> children = new HashMap<>();
14 private T value;
15
16 HashTreeNode(Entry<?> entry) {
17 this.entry = entry;
18 }
19
20 void putValue(T value) {
21 this.value = value;
22 }
23
24 T removeValue() {
25 T value = this.value;
26 this.value = null;
27 return value;
28 }
29
30 HashTreeNode<T> getChild(Entry<?> entry, boolean create) {
31 if (create) {
32 return children.computeIfAbsent(entry, HashTreeNode::new);
33 } else {
34 return children.get(entry);
35 }
36 }
37
38 void remove(Entry<?> entry) {
39 children.remove(entry);
40 }
41
42 @Override
43 @Nullable
44 public T getValue() {
45 return value;
46 }
47
48 @Override
49 public Entry<?> getEntry() {
50 return entry;
51 }
52
53 @Override
54 public boolean isEmpty() {
55 return children.isEmpty() && value == null;
56 }
57
58 @Override
59 public Collection<Entry<?>> getChildren() {
60 return children.keySet();
61 }
62
63 @Override
64 public Collection<? extends EntryTreeNode<T>> getChildNodes() {
65 return children.values();
66 }
67
68 @Override
69 public Iterator<HashTreeNode<T>> iterator() {
70 return children.values().iterator();
71 }
72}
diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
index 31c86918..0534edda 100644
--- a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
@@ -1,4 +1,4 @@
1package cuchaz.enigma.bytecode; 1package cuchaz.enigma.translation.representation;
2 2
3import cuchaz.enigma.analysis.Access; 3import cuchaz.enigma.analysis.Access;
4import org.objectweb.asm.Opcodes; 4import org.objectweb.asm.Opcodes;
@@ -6,6 +6,9 @@ import org.objectweb.asm.Opcodes;
6import java.lang.reflect.Modifier; 6import java.lang.reflect.Modifier;
7 7
8public class AccessFlags { 8public class AccessFlags {
9 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE);
10 public static final AccessFlags PUBLIC = new AccessFlags(Opcodes.ACC_PUBLIC);
11
9 private int flags; 12 private int flags;
10 13
11 public AccessFlags(int flags) { 14 public AccessFlags(int flags) {
@@ -40,6 +43,10 @@ public class AccessFlags {
40 return (flags & Opcodes.ACC_BRIDGE) != 0; 43 return (flags & Opcodes.ACC_BRIDGE) != 0;
41 } 44 }
42 45
46 public boolean isFinal() {
47 return (flags & Opcodes.ACC_FINAL) != 0;
48 }
49
43 public AccessFlags setPrivate() { 50 public AccessFlags setPrivate() {
44 this.setVisibility(Opcodes.ACC_PRIVATE); 51 this.setVisibility(Opcodes.ACC_PRIVATE);
45 return this; 52 return this;
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
index 0fc03517..c59751f9 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
@@ -9,17 +9,22 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.translation.representation;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.EntryMap;
20import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import cuchaz.enigma.utils.Utils; 21import cuchaz.enigma.utils.Utils;
17 22
18import java.util.ArrayList; 23import java.util.ArrayList;
19import java.util.List; 24import java.util.List;
20import java.util.function.Function; 25import java.util.function.Function;
21 26
22public class MethodDescriptor { 27public class MethodDescriptor implements Translatable {
23 28
24 private List<TypeDescriptor> argumentDescs; 29 private List<TypeDescriptor> argumentDescs;
25 private TypeDescriptor returnDesc; 30 private TypeDescriptor returnDesc;
@@ -111,4 +116,17 @@ public class MethodDescriptor {
111 } 116 }
112 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper)); 117 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
113 } 118 }
119
120 @Override
121 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
122 List<TypeDescriptor> translatedArguments = new ArrayList<>(argumentDescs.size());
123 for (TypeDescriptor argument : argumentDescs) {
124 translatedArguments.add(translator.translate(argument));
125 }
126 return new MethodDescriptor(translatedArguments, translator.translate(returnDesc));
127 }
128
129 public boolean canConflictWith(MethodDescriptor descriptor) {
130 return descriptor.argumentDescs.equals(argumentDescs);
131 }
114} 132}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java
index 73770c53..9c9fa3d3 100644
--- a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java
@@ -9,15 +9,12 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping.entry; 12package cuchaz.enigma.translation.representation;
13 13
14import com.strobel.assembler.metadata.FieldDefinition; 14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MemberReference; 15import com.strobel.assembler.metadata.MemberReference;
16import com.strobel.assembler.metadata.MethodDefinition; 16import com.strobel.assembler.metadata.MethodDefinition;
17import cuchaz.enigma.bytecode.AccessFlags; 17import cuchaz.enigma.translation.representation.entry.*;
18import cuchaz.enigma.mapping.MethodDescriptor;
19import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.TypeDescriptor;
21 18
22public class ProcyonEntryFactory { 19public class ProcyonEntryFactory {
23 private final ReferencedEntryPool entryPool; 20 private final ReferencedEntryPool entryPool;
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java
index 12b39554..631b3754 100644
--- a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java
@@ -9,10 +9,11 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping.entry; 12package cuchaz.enigma.translation.representation;
13 13
14import cuchaz.enigma.mapping.MethodDescriptor; 14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15import cuchaz.enigma.mapping.TypeDescriptor; 15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
16 17
17import java.util.HashMap; 18import java.util.HashMap;
18import java.util.Map; 19import java.util.Map;
@@ -38,7 +39,7 @@ public class ReferencedEntryPool {
38 39
39 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) { 40 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
40 String key = name + desc.toString(); 41 String key = name + desc.toString();
41 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc)); 42 return getClassMethods(ownerEntry.getFullName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
42 } 43 }
43 44
44 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) { 45 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
@@ -46,7 +47,7 @@ public class ReferencedEntryPool {
46 } 47 }
47 48
48 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) { 49 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
49 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc)); 50 return getClassFields(ownerEntry.getFullName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
50 } 51 }
51 52
52 private Map<String, MethodEntry> getClassMethods(String name) { 53 private Map<String, MethodEntry> getClassMethods(String name) {
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/translation/representation/Signature.java
index 071e4afa..dc241b7a 100644
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/Signature.java
@@ -1,6 +1,12 @@
1package cuchaz.enigma.mapping; 1package cuchaz.enigma.translation.representation;
2 2
3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; 3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
4import cuchaz.enigma.translation.Translatable;
5import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryResolver;
8import cuchaz.enigma.translation.mapping.EntryMap;
9import cuchaz.enigma.translation.representation.entry.ClassEntry;
4import org.objectweb.asm.signature.SignatureReader; 10import org.objectweb.asm.signature.SignatureReader;
5import org.objectweb.asm.signature.SignatureVisitor; 11import org.objectweb.asm.signature.SignatureVisitor;
6import org.objectweb.asm.signature.SignatureWriter; 12import org.objectweb.asm.signature.SignatureWriter;
@@ -8,7 +14,7 @@ import org.objectweb.asm.signature.SignatureWriter;
8import java.util.function.Function; 14import java.util.function.Function;
9import java.util.regex.Pattern; 15import java.util.regex.Pattern;
10 16
11public class Signature { 17public class Signature implements Translatable {
12 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*"); 18 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
13 19
14 private final String signature; 20 private final String signature;
@@ -79,4 +85,9 @@ public class Signature {
79 public String toString() { 85 public String toString() {
80 return signature; 86 return signature;
81 } 87 }
88
89 @Override
90 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
91 return remap(name -> translator.translate(new ClassEntry(name)).getFullName());
92 }
82} 93}
diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
index 6e58aa0f..f7ba849e 100644
--- a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
@@ -9,16 +9,21 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.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 cuchaz.enigma.mapping.entry.ClassEntry; 16import cuchaz.enigma.translation.Translatable;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.EntryMap;
21import cuchaz.enigma.translation.representation.entry.ClassEntry;
17 22
18import java.util.Map; 23import java.util.Map;
19import java.util.function.Function; 24import java.util.function.Function;
20 25
21public class TypeDescriptor { 26public class TypeDescriptor implements Translatable {
22 27
23 protected final String desc; 28 protected final String desc;
24 29
@@ -164,7 +169,7 @@ public class TypeDescriptor {
164 if (!isArray()) { 169 if (!isArray()) {
165 throw new IllegalStateException("not an array"); 170 throw new IllegalStateException("not an array");
166 } 171 }
167 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length())); 172 return new TypeDescriptor(this.desc.substring(getArrayDimension()));
168 } 173 }
169 174
170 public boolean containsType() { 175 public boolean containsType() {
@@ -188,7 +193,7 @@ public class TypeDescriptor {
188 public TypeDescriptor remap(Function<String, String> remapper) { 193 public TypeDescriptor remap(Function<String, String> remapper) {
189 String desc = this.desc; 194 String desc = this.desc;
190 if (isType() || (isArray() && containsType())) { 195 if (isType() || (isArray() && containsType())) {
191 String replacedName = remapper.apply(this.getTypeEntry().getName()); 196 String replacedName = remapper.apply(this.getTypeEntry().getFullName());
192 if (replacedName != null) { 197 if (replacedName != null) {
193 if (this.isType()) { 198 if (this.isType()) {
194 desc = "L" + replacedName + ";"; 199 desc = "L" + replacedName + ";";
@@ -222,6 +227,11 @@ public class TypeDescriptor {
222 } 227 }
223 } 228 }
224 229
230 @Override
231 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
232 return remap(name -> translator.translate(new ClassEntry(name)).getFullName());
233 }
234
225 public enum Primitive { 235 public enum Primitive {
226 BYTE('B'), 236 BYTE('B'),
227 CHARACTER('C'), 237 CHARACTER('C'),
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
new file mode 100644
index 00000000..b9391b04
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
@@ -0,0 +1,92 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import com.strobel.assembler.metadata.TypeDefinition;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.representation.AccessFlags;
19import cuchaz.enigma.translation.representation.Signature;
20
21import javax.annotation.Nullable;
22import java.util.Arrays;
23
24public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
25 private final AccessFlags access;
26 private final Signature signature;
27 private final ClassEntry superClass;
28 private final ClassEntry[] interfaces;
29
30 public ClassDefEntry(String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces) {
31 this(getOuterClass(className), getInnerName(className), signature, access, superClass, interfaces);
32 }
33
34 public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces) {
35 super(parent, className);
36 Preconditions.checkNotNull(signature, "Class signature cannot be null");
37 Preconditions.checkNotNull(access, "Class access cannot be null");
38
39 this.signature = signature;
40 this.access = access;
41 this.superClass = superClass;
42 this.interfaces = interfaces != null ? interfaces : new ClassEntry[0];
43 }
44
45 public static ClassDefEntry parse(int access, String name, String signature, String superName, String[] interfaces) {
46 ClassEntry superClass = superName != null ? new ClassEntry(superName) : null;
47 ClassEntry[] interfaceClasses = Arrays.stream(interfaces).map(ClassEntry::new).toArray(ClassEntry[]::new);
48 return new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access), superClass, interfaceClasses);
49 }
50
51 public static ClassDefEntry parse(TypeDefinition def) {
52 String name = def.getInternalName();
53 Signature signature = Signature.createSignature(def.getSignature());
54 AccessFlags access = new AccessFlags(def.getModifiers());
55 ClassEntry superClass = def.getBaseType() != null ? ClassEntry.parse(def.getBaseType()) : null;
56 ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(ClassEntry::parse).toArray(ClassEntry[]::new);
57 return new ClassDefEntry(name, signature, access, superClass, interfaces);
58 }
59
60 public Signature getSignature() {
61 return signature;
62 }
63
64 @Override
65 public AccessFlags getAccess() {
66 return access;
67 }
68
69 @Nullable
70 public ClassEntry getSuperClass() {
71 return superClass;
72 }
73
74 public ClassEntry[] getInterfaces() {
75 return interfaces;
76 }
77
78 @Override
79 public ClassDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
80 Signature translatedSignature = translator.translate(signature);
81 String translatedName = mapping != null ? mapping.getTargetName() : name;
82 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
83 ClassEntry translatedSuper = translator.translate(superClass);
84 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new);
85 return new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces);
86 }
87
88 @Override
89 public ClassDefEntry withParent(ClassEntry parent) {
90 return new ClassDefEntry(parent, name, signature, access, superClass, interfaces);
91 }
92}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
new file mode 100644
index 00000000..dcbb8d98
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
@@ -0,0 +1,180 @@
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.entry;
13
14import com.strobel.assembler.metadata.TypeReference;
15import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.mapping.NameValidator;
19
20import javax.annotation.Nullable;
21import java.util.List;
22import java.util.Objects;
23
24public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<ClassEntry> {
25 private final String fullName;
26
27 public ClassEntry(String className) {
28 this(getOuterClass(className), getInnerName(className));
29 }
30
31 public ClassEntry(@Nullable ClassEntry parent, String className) {
32 super(parent, className);
33 if (parent != null) {
34 fullName = parent.getFullName() + "$" + name;
35 } else {
36 fullName = name;
37 }
38
39 if (parent == null && className.indexOf('.') >= 0) {
40 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
41 }
42 }
43
44 public static ClassEntry parse(TypeReference typeReference) {
45 return new ClassEntry(typeReference.getInternalName());
46 }
47
48 @Override
49 public Class<ClassEntry> getParentType() {
50 return ClassEntry.class;
51 }
52
53 @Override
54 public String getName() {
55 return this.name;
56 }
57
58 public String getFullName() {
59 return fullName;
60 }
61
62 @Override
63 public ClassEntry translate(Translator translator, @Nullable EntryMapping mapping) {
64 String translatedName = mapping != null ? mapping.getTargetName() : name;
65 return new ClassEntry(parent, translatedName);
66 }
67
68 @Override
69 public ClassEntry getContainingClass() {
70 return this;
71 }
72
73 @Override
74 public int hashCode() {
75 return fullName.hashCode();
76 }
77
78 @Override
79 public boolean equals(Object other) {
80 return other instanceof ClassEntry && equals((ClassEntry) other);
81 }
82
83 public boolean equals(ClassEntry other) {
84 return other != null && Objects.equals(parent, other.parent) && this.name.equals(other.name);
85 }
86
87 @Override
88 public boolean canConflictWith(Entry<?> entry) {
89 return true;
90 }
91
92 @Override
93 public void validateName(String name) throws IllegalNameException {
94 NameValidator.validateClassName(name, !isInnerClass());
95 }
96
97 @Override
98 public ClassEntry withParent(ClassEntry parent) {
99 return new ClassEntry(parent, name);
100 }
101
102 @Override
103 public String toString() {
104 return getFullName();
105 }
106
107 public String getPackageName() {
108 return getPackageName(this.name);
109 }
110
111 public String getSimpleName() {
112 int packagePos = name.lastIndexOf('/');
113 if (packagePos > 0) {
114 return name.substring(packagePos + 1);
115 }
116 return name;
117 }
118
119 public boolean isInnerClass() {
120 return parent != null;
121 }
122
123 @Nullable
124 public ClassEntry getOuterClass() {
125 return parent;
126 }
127
128 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
129 assert (classChain.contains(this));
130 StringBuilder buf = new StringBuilder();
131 for (ClassEntry chainEntry : classChain) {
132 if (buf.length() == 0) {
133 buf.append(chainEntry.getFullName());
134 } else {
135 buf.append("$");
136 buf.append(chainEntry.getSimpleName());
137 }
138
139 if (chainEntry == this) {
140 break;
141 }
142 }
143 return new ClassEntry(buf.toString());
144 }
145
146 public boolean isJre() {
147 String packageName = getPackageName();
148 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
149 }
150
151 public static String getPackageName(String name) {
152 int pos = name.lastIndexOf('/');
153 if (pos > 0) {
154 return name.substring(0, pos);
155 }
156 return null;
157 }
158
159 @Nullable
160 public static ClassEntry getOuterClass(String name) {
161 int index = name.lastIndexOf('$');
162 if (index >= 0) {
163 return new ClassEntry(name.substring(0, index));
164 }
165 return null;
166 }
167
168 public static String getInnerName(String name) {
169 int innerClassPos = name.lastIndexOf('$');
170 if (innerClassPos > 0) {
171 return name.substring(innerClassPos + 1);
172 }
173 return name;
174 }
175
176 @Override
177 public int compareTo(ClassEntry entry) {
178 return name.compareTo(entry.name);
179 }
180}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java
new file mode 100644
index 00000000..82536c73
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java
@@ -0,0 +1,7 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4
5public interface DefEntry<P extends Entry<?>> extends Entry<P> {
6 AccessFlags getAccess();
7}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
new file mode 100644
index 00000000..1a2ca785
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
@@ -0,0 +1,99 @@
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.entry;
13
14import cuchaz.enigma.throwables.IllegalNameException;
15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.mapping.NameValidator;
17
18import javax.annotation.Nullable;
19import java.util.ArrayList;
20import java.util.List;
21
22public interface Entry<P extends Entry<?>> extends Translatable {
23 String getName();
24
25 @Nullable
26 P getParent();
27
28 Class<P> getParentType();
29
30 Entry<P> withParent(P parent);
31
32 boolean canConflictWith(Entry<?> entry);
33
34 @Nullable
35 default ClassEntry getContainingClass() {
36 P parent = getParent();
37 if (parent == null) {
38 return null;
39 }
40 if (parent instanceof ClassEntry) {
41 return (ClassEntry) parent;
42 }
43 return parent.getContainingClass();
44 }
45
46 default List<Entry<?>> getAncestry() {
47 P parent = getParent();
48 List<Entry<?>> entries = new ArrayList<>();
49 if (parent != null) {
50 entries.addAll(parent.getAncestry());
51 }
52 entries.add(this);
53 return entries;
54 }
55
56 @Nullable
57 @SuppressWarnings("unchecked")
58 default <E extends Entry<?>> E findAncestor(Class<E> type) {
59 List<Entry<?>> ancestry = getAncestry();
60 for (int i = ancestry.size() - 1; i >= 0; i--) {
61 Entry<?> ancestor = ancestry.get(i);
62 if (type.isAssignableFrom(ancestor.getClass())) {
63 return (E) ancestor;
64 }
65 }
66 return null;
67 }
68
69 @SuppressWarnings("unchecked")
70 default <E extends Entry<?>> Entry<P> replaceAncestor(E target, E replacement) {
71 if (replacement.equals(target)) {
72 return this;
73 }
74
75 if (equals(target)) {
76 return (Entry<P>) replacement;
77 }
78
79 P parent = getParent();
80 if (parent == null) {
81 return this;
82 }
83
84 return withParent((P) parent.replaceAncestor(target, replacement));
85 }
86
87 default void validateName(String name) throws IllegalNameException {
88 NameValidator.validateIdentifier(name);
89 }
90
91 @SuppressWarnings("unchecked")
92 @Nullable
93 default <C extends Entry<?>> Entry<C> castParent(Class<C> parentType) {
94 if (parentType.equals(getParentType())) {
95 return (Entry<C>) this;
96 }
97 return null;
98 }
99}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
new file mode 100644
index 00000000..d487f71f
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
@@ -0,0 +1,61 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.AccessFlags;
18import cuchaz.enigma.translation.representation.Signature;
19import cuchaz.enigma.translation.representation.TypeDescriptor;
20
21import javax.annotation.Nullable;
22
23public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
24 private final AccessFlags access;
25 private final Signature signature;
26
27 public FieldDefEntry(ClassEntry owner, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
28 super(owner, name, desc);
29 Preconditions.checkNotNull(access, "Field access cannot be null");
30 Preconditions.checkNotNull(signature, "Field signature cannot be null");
31 this.access = access;
32 this.signature = signature;
33 }
34
35 public static FieldDefEntry parse(ClassEntry owner, int access, String name, String desc, String signature) {
36 return new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access));
37 }
38
39 @Override
40 public AccessFlags getAccess() {
41 return access;
42 }
43
44 public Signature getSignature() {
45 return signature;
46 }
47
48 @Override
49 public FieldDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
50 TypeDescriptor translatedDesc = translator.translate(desc);
51 Signature translatedSignature = translator.translate(signature);
52 String translatedName = mapping != null ? mapping.getTargetName() : name;
53 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
54 return new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess);
55 }
56
57 @Override
58 public FieldDefEntry withParent(ClassEntry owner) {
59 return new FieldDefEntry(owner, this.name, this.desc, signature, access);
60 }
61}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
new file mode 100644
index 00000000..2ec24719
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
@@ -0,0 +1,86 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.TypeDescriptor;
18import cuchaz.enigma.utils.Utils;
19
20import javax.annotation.Nullable;
21
22public class FieldEntry extends ParentedEntry<ClassEntry> implements Comparable<FieldEntry> {
23 protected final TypeDescriptor desc;
24
25 public FieldEntry(ClassEntry parent, String name, TypeDescriptor desc) {
26 super(parent, name);
27
28 Preconditions.checkNotNull(parent, "Owner cannot be null");
29 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
30
31 this.desc = desc;
32 }
33
34 public static FieldEntry parse(String owner, String name, String desc) {
35 return new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc));
36 }
37
38 @Override
39 public Class<ClassEntry> getParentType() {
40 return ClassEntry.class;
41 }
42
43 public TypeDescriptor getDesc() {
44 return this.desc;
45 }
46
47 @Override
48 public FieldEntry withParent(ClassEntry parent) {
49 return new FieldEntry(parent, this.name, this.desc);
50 }
51
52 @Override
53 protected FieldEntry translate(Translator translator, @Nullable EntryMapping mapping) {
54 String translatedName = mapping != null ? mapping.getTargetName() : name;
55 return new FieldEntry(parent, translatedName, translator.translate(desc));
56 }
57
58 @Override
59 public int hashCode() {
60 return Utils.combineHashesOrdered(this.parent, this.name, this.desc);
61 }
62
63 @Override
64 public boolean equals(Object other) {
65 return other instanceof FieldEntry && equals((FieldEntry) other);
66 }
67
68 public boolean equals(FieldEntry other) {
69 return this.parent.equals(other.parent) && name.equals(other.name) && desc.equals(other.desc);
70 }
71
72 @Override
73 public boolean canConflictWith(Entry<?> entry) {
74 return entry instanceof FieldEntry && ((FieldEntry) entry).parent.equals(parent);
75 }
76
77 @Override
78 public String toString() {
79 return this.parent.getFullName() + "." + this.name + ":" + this.desc;
80 }
81
82 @Override
83 public int compareTo(FieldEntry entry) {
84 return (name + desc.toString()).compareTo(entry.name + entry.desc.toString());
85 }
86}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
new file mode 100644
index 00000000..86bdf61c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
@@ -0,0 +1,45 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.representation.TypeDescriptor;
7
8import javax.annotation.Nullable;
9
10/**
11 * TypeDescriptor...
12 * Created by Thog
13 * 19/10/2016
14 */
15public class LocalVariableDefEntry extends LocalVariableEntry {
16 protected final TypeDescriptor desc;
17
18 public LocalVariableDefEntry(MethodEntry ownerEntry, int index, String name, boolean parameter, TypeDescriptor desc) {
19 super(ownerEntry, index, name, parameter);
20 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
21
22 this.desc = desc;
23 }
24
25 public TypeDescriptor getDesc() {
26 return desc;
27 }
28
29 @Override
30 public LocalVariableDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
31 TypeDescriptor translatedDesc = translator.translate(desc);
32 String translatedName = mapping != null ? mapping.getTargetName() : name;
33 return new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc);
34 }
35
36 @Override
37 public LocalVariableDefEntry withParent(MethodEntry entry) {
38 return new LocalVariableDefEntry(entry, index, name, parameter, desc);
39 }
40
41 @Override
42 public String toString() {
43 return this.parent + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
44 }
45}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
new file mode 100644
index 00000000..df96b599
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
@@ -0,0 +1,92 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.utils.Utils;
7
8import javax.annotation.Nullable;
9
10/**
11 * TypeDescriptor...
12 * Created by Thog
13 * 19/10/2016
14 */
15public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Comparable<LocalVariableEntry> {
16
17 protected final int index;
18 protected final boolean parameter;
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) {
26 super(parent, name);
27
28 Preconditions.checkNotNull(parent, "Variable owner cannot be null");
29 Preconditions.checkArgument(index >= 0, "Index must be positive");
30
31 this.index = index;
32 this.parameter = parameter;
33 }
34
35 @Override
36 public Class<MethodEntry> getParentType() {
37 return MethodEntry.class;
38 }
39
40 public boolean isParameter() {
41 return this.parameter;
42 }
43
44 public int getIndex() {
45 return index;
46 }
47
48 @Override
49 public String getName() {
50 return this.name;
51 }
52
53 @Override
54 public LocalVariableEntry translate(Translator translator, @Nullable EntryMapping mapping) {
55 String translatedName = mapping != null ? mapping.getTargetName() : name;
56 return new LocalVariableEntry(parent, index, translatedName, parameter);
57 }
58
59 @Override
60 public LocalVariableEntry withParent(MethodEntry parent) {
61 return new LocalVariableEntry(parent, index, name, parameter);
62 }
63
64 @Override
65 public int hashCode() {
66 return Utils.combineHashesOrdered(this.parent, this.index);
67 }
68
69 @Override
70 public boolean equals(Object other) {
71 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
72 }
73
74 public boolean equals(LocalVariableEntry other) {
75 return this.parent.equals(other.parent) && this.index == other.index;
76 }
77
78 @Override
79 public boolean canConflictWith(Entry<?> entry) {
80 return entry instanceof LocalVariableEntry && ((LocalVariableEntry) entry).parent.equals(parent);
81 }
82
83 @Override
84 public String toString() {
85 return this.parent + "(" + this.index + ":" + this.name + ")";
86 }
87
88 @Override
89 public int compareTo(LocalVariableEntry entry) {
90 return Integer.compare(index, entry.index);
91 }
92}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
index fa9e6685..3ecd470f 100644
--- a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
@@ -9,26 +9,33 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags; 15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.mapping.MethodDescriptor; 16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.mapping.Signature; 17import cuchaz.enigma.translation.representation.AccessFlags;
18import cuchaz.enigma.translation.representation.MethodDescriptor;
19import cuchaz.enigma.translation.representation.Signature;
18 20
19public class MethodDefEntry extends MethodEntry implements DefEntry { 21import javax.annotation.Nullable;
20 22
23public class MethodDefEntry extends MethodEntry implements DefEntry<ClassEntry> {
21 private final AccessFlags access; 24 private final AccessFlags access;
22 private final Signature signature; 25 private final Signature signature;
23 26
24 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) { 27 public MethodDefEntry(ClassEntry owner, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) {
25 super(classEntry, name, descriptor); 28 super(owner, name, descriptor);
26 Preconditions.checkNotNull(access, "Method access cannot be null"); 29 Preconditions.checkNotNull(access, "Method access cannot be null");
27 Preconditions.checkNotNull(signature, "Method signature cannot be null"); 30 Preconditions.checkNotNull(signature, "Method signature cannot be null");
28 this.access = access; 31 this.access = access;
29 this.signature = signature; 32 this.signature = signature;
30 } 33 }
31 34
35 public static MethodDefEntry parse(ClassEntry owner, int access, String name, String desc, String signature) {
36 return new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access));
37 }
38
32 @Override 39 @Override
33 public AccessFlags getAccess() { 40 public AccessFlags getAccess() {
34 return access; 41 return access;
@@ -39,8 +46,17 @@ public class MethodDefEntry extends MethodEntry implements DefEntry {
39 } 46 }
40 47
41 @Override 48 @Override
42 public MethodDefEntry updateOwnership(ClassEntry classEntry) { 49 public MethodDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
43 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, signature, access); 50 MethodDescriptor translatedDesc = translator.translate(descriptor);
51 Signature translatedSignature = translator.translate(signature);
52 String translatedName = mapping != null ? mapping.getTargetName() : name;
53 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
54 return new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess);
55 }
56
57 @Override
58 public MethodDefEntry withParent(ClassEntry parent) {
59 return new MethodDefEntry(new ClassEntry(parent.getFullName()), name, descriptor, signature, access);
44 } 60 }
45 61
46 public int getArgumentIndex(ClassDefEntry ownerEntry, int localVariableIndex) { 62 public int getArgumentIndex(ClassDefEntry ownerEntry, int localVariableIndex) {
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
new file mode 100644
index 00000000..3a1dbb32
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
@@ -0,0 +1,95 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.MethodDescriptor;
18import cuchaz.enigma.utils.Utils;
19
20import javax.annotation.Nullable;
21
22public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable<MethodEntry> {
23
24 protected final MethodDescriptor descriptor;
25
26 public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor) {
27 super(parent, name);
28
29 Preconditions.checkNotNull(parent, "Parent cannot be null");
30 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
31
32 this.descriptor = descriptor;
33 }
34
35 public static MethodEntry parse(String owner, String name, String desc) {
36 return new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc));
37 }
38
39 @Override
40 public Class<ClassEntry> getParentType() {
41 return ClassEntry.class;
42 }
43
44 public MethodDescriptor getDesc() {
45 return this.descriptor;
46 }
47
48 public boolean isConstructor() {
49 return name.equals("<init>") || name.equals("<clinit>");
50 }
51
52 @Override
53 public MethodEntry translate(Translator translator, @Nullable EntryMapping mapping) {
54 String translatedName = mapping != null ? mapping.getTargetName() : name;
55 return new MethodEntry(parent, translatedName, translator.translate(descriptor));
56 }
57
58 @Override
59 public MethodEntry withParent(ClassEntry parent) {
60 return new MethodEntry(new ClassEntry(parent.getFullName()), name, descriptor);
61 }
62
63 @Override
64 public int hashCode() {
65 return Utils.combineHashesOrdered(this.parent, this.name, this.descriptor);
66 }
67
68 @Override
69 public boolean equals(Object other) {
70 return other instanceof MethodEntry && equals((MethodEntry) other);
71 }
72
73 public boolean equals(MethodEntry other) {
74 return this.parent.equals(other.getParent()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
75 }
76
77 @Override
78 public boolean canConflictWith(Entry<?> entry) {
79 if (entry instanceof MethodEntry) {
80 MethodEntry methodEntry = (MethodEntry) entry;
81 return methodEntry.parent.equals(parent) && methodEntry.descriptor.canConflictWith(descriptor);
82 }
83 return false;
84 }
85
86 @Override
87 public String toString() {
88 return this.parent.getFullName() + "." + this.name + this.descriptor;
89 }
90
91 @Override
92 public int compareTo(MethodEntry entry) {
93 return (name + descriptor.toString()).compareTo(entry.name + entry.descriptor.toString());
94 }
95}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
new file mode 100644
index 00000000..7ba7c196
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
@@ -0,0 +1,71 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMap;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.ResolutionStrategy;
21
22import javax.annotation.Nullable;
23
24public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
25 protected final P parent;
26 protected final String name;
27
28 protected ParentedEntry(P parent, String name) {
29 this.parent = parent;
30 this.name = name;
31
32 Preconditions.checkNotNull(name, "Name cannot be null");
33 }
34
35 @Override
36 public abstract ParentedEntry<P> withParent(P parent);
37
38 protected abstract ParentedEntry<P> translate(Translator translator, @Nullable EntryMapping mapping);
39
40 @Override
41 public String getName() {
42 return name;
43 }
44
45 @Override
46 @Nullable
47 public P getParent() {
48 return parent;
49 }
50
51 @Override
52 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
53 P parent = getParent();
54 EntryMapping mapping = resolveMapping(resolver, mappings);
55 if (parent == null) {
56 return translate(translator, mapping);
57 }
58 P translatedParent = translator.translate(parent);
59 return withParent(translatedParent).translate(translator, mapping);
60 }
61
62 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings) {
63 for (ParentedEntry<P> entry : resolver.resolveEntry(this, ResolutionStrategy.RESOLVE_ROOT)) {
64 EntryMapping mapping = mappings.get(entry);
65 if (mapping != null) {
66 return mapping;
67 }
68 }
69 return null;
70 }
71}
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java
index 9babf1e3..25cb60c5 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfed.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfed.java
@@ -11,9 +11,8 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.analysis.ParsedJar; 14import cuchaz.enigma.analysis.ParsedJar;
16import cuchaz.enigma.mapping.entry.ReferencedEntryPool; 15import cuchaz.enigma.analysis.index.JarIndex;
17import org.junit.BeforeClass; 16import org.junit.BeforeClass;
18import org.junit.Test; 17import org.junit.Test;
19 18
@@ -32,13 +31,13 @@ public class TestDeobfed {
32 public static void beforeClass() 31 public static void beforeClass()
33 throws Exception { 32 throws Exception {
34 jar = new ParsedJar(new JarFile("build/test-deobf/translation.jar")); 33 jar = new ParsedJar(new JarFile("build/test-deobf/translation.jar"));
35 index = new JarIndex(new ReferencedEntryPool()); 34 index = JarIndex.empty();
36 index.indexJar(jar, true); 35 index.indexJar(jar, s -> {});
37 } 36 }
38 37
39 @Test 38 @Test
40 public void obfEntries() { 39 public void obfEntries() {
41 assertThat(index.getObfClassEntries(), containsInAnyOrder( 40 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(
42 newClass("cuchaz/enigma/inputs/Keep"), 41 newClass("cuchaz/enigma/inputs/Keep"),
43 newClass("a"), 42 newClass("a"),
44 newClass("b"), 43 newClass("b"),
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
index 63a6f552..5f117213 100644
--- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java
+++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java
@@ -12,7 +12,7 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16import org.junit.Test; 16import org.junit.Test;
17 17
18import java.io.IOException; 18import java.io.IOException;
diff --git a/src/test/java/cuchaz/enigma/TestEntryFactory.java b/src/test/java/cuchaz/enigma/TestEntryFactory.java
index 4f52609e..9e1425a2 100644
--- a/src/test/java/cuchaz/enigma/TestEntryFactory.java
+++ b/src/test/java/cuchaz/enigma/TestEntryFactory.java
@@ -12,10 +12,10 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.translation.representation.*;
16import cuchaz.enigma.mapping.entry.ClassEntry; 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.FieldEntry; 17import cuchaz.enigma.translation.representation.entry.FieldEntry;
18import cuchaz.enigma.mapping.entry.MethodEntry; 18import cuchaz.enigma.translation.representation.entry.MethodEntry;
19 19
20public class TestEntryFactory { 20public class TestEntryFactory {
21 21
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java
index 843a63c7..0319cf61 100644
--- a/src/test/java/cuchaz/enigma/TestInnerClasses.java
+++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java
@@ -11,20 +11,16 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.analysis.ParsedJar; 14import cuchaz.enigma.analysis.ParsedJar;
16import cuchaz.enigma.mapping.entry.ClassEntry; 15import cuchaz.enigma.analysis.index.JarIndex;
17import cuchaz.enigma.mapping.entry.ReferencedEntryPool; 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import org.junit.Test; 17import org.junit.Test;
19 18
20import java.util.jar.JarFile; 19import java.util.jar.JarFile;
21 20
22import static cuchaz.enigma.TestEntryFactory.newClass; 21import static cuchaz.enigma.TestEntryFactory.newClass;
23import static org.hamcrest.MatcherAssert.assertThat; 22import static org.hamcrest.MatcherAssert.assertThat;
24import static org.hamcrest.Matchers.containsInAnyOrder;
25import static org.hamcrest.Matchers.empty;
26import static org.hamcrest.Matchers.is; 23import static org.hamcrest.Matchers.is;
27import static org.hamcrest.Matchers.nullValue;
28 24
29public class TestInnerClasses { 25public class TestInnerClasses {
30 26
@@ -41,23 +37,19 @@ public class TestInnerClasses {
41 37
42 public TestInnerClasses() 38 public TestInnerClasses()
43 throws Exception { 39 throws Exception {
44 index = new JarIndex(new ReferencedEntryPool()); 40 index = JarIndex.empty();
45 ParsedJar jar = new ParsedJar(new JarFile("build/test-obf/innerClasses.jar")); 41 ParsedJar jar = new ParsedJar(new JarFile("build/test-obf/innerClasses.jar"));
46 index.indexJar(jar, true); 42 index.indexJar(jar, s -> {});
47 deobfuscator = new Deobfuscator(jar); 43 deobfuscator = new Deobfuscator(jar);
48 } 44 }
49 45
50 @Test 46 @Test
51 public void simple() { 47 public void simple() {
52 assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter));
53 assertThat(index.getInnerClasses(SimpleOuter), containsInAnyOrder(SimpleInner));
54 decompile(SimpleOuter); 48 decompile(SimpleOuter);
55 } 49 }
56 50
57 @Test 51 @Test
58 public void constructorArgs() { 52 public void constructorArgs() {
59 assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter));
60 assertThat(index.getInnerClasses(ConstructorArgsOuter), containsInAnyOrder(ConstructorArgsInner));
61 decompile(ConstructorArgsOuter); 53 decompile(ConstructorArgsOuter);
62 } 54 }
63 55
@@ -65,33 +57,25 @@ public class TestInnerClasses {
65 public void classTree() { 57 public void classTree() {
66 58
67 // root level 59 // root level
68 assertThat(index.containsObfClass(ClassTreeRoot), is(true)); 60 assertThat(index.getEntryIndex().hasClass(ClassTreeRoot), is(true));
69 assertThat(index.getOuterClass(ClassTreeRoot), is(nullValue()));
70 assertThat(index.getInnerClasses(ClassTreeRoot), containsInAnyOrder(ClassTreeLevel1));
71 61
72 // level 1 62 // level 1
73 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 63 ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
74 + "$" + ClassTreeLevel1.getInnermostClassName()); 64 + "$" + ClassTreeLevel1.getSimpleName());
75 assertThat(index.containsObfClass(fullClassEntry), is(true)); 65 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
76 assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot));
77 assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2));
78 66
79 // level 2 67 // level 2
80 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 68 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
81 + "$" + ClassTreeLevel1.getInnermostClassName() 69 + "$" + ClassTreeLevel1.getSimpleName()
82 + "$" + ClassTreeLevel2.getInnermostClassName()); 70 + "$" + ClassTreeLevel2.getSimpleName());
83 assertThat(index.containsObfClass(fullClassEntry), is(true)); 71 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
84 assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1));
85 assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3));
86 72
87 // level 3 73 // level 3
88 fullClassEntry = new ClassEntry(ClassTreeRoot.getName() 74 fullClassEntry = new ClassEntry(ClassTreeRoot.getName()
89 + "$" + ClassTreeLevel1.getInnermostClassName() 75 + "$" + ClassTreeLevel1.getSimpleName()
90 + "$" + ClassTreeLevel2.getInnermostClassName() 76 + "$" + ClassTreeLevel2.getSimpleName()
91 + "$" + ClassTreeLevel3.getInnermostClassName()); 77 + "$" + ClassTreeLevel3.getSimpleName());
92 assertThat(index.containsObfClass(fullClassEntry), is(true)); 78 assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true));
93 assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2));
94 assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty()));
95 } 79 }
96 80
97 private void decompile(ClassEntry classEntry) { 81 private void decompile(ClassEntry classEntry) {
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
index 763639a4..c3f3b669 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java
@@ -12,12 +12,11 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.EntryReference; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.analysis.ParsedJar; 15import cuchaz.enigma.analysis.ParsedJar;
17import cuchaz.enigma.mapping.entry.ClassEntry; 16import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.mapping.entry.MethodDefEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.mapping.entry.MethodEntry; 18import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
20import cuchaz.enigma.mapping.entry.ReferencedEntryPool; 19import cuchaz.enigma.translation.representation.entry.MethodEntry;
21import org.junit.Test; 20import org.junit.Test;
22 21
23import java.io.File; 22import java.io.File;
@@ -41,13 +40,13 @@ public class TestJarIndexConstructorReferences {
41 public TestJarIndexConstructorReferences() 40 public TestJarIndexConstructorReferences()
42 throws Exception { 41 throws Exception {
43 File jarFile = new File("build/test-obf/constructors.jar"); 42 File jarFile = new File("build/test-obf/constructors.jar");
44 index = new JarIndex(new ReferencedEntryPool()); 43 index = JarIndex.empty();
45 index.indexJar(new ParsedJar(new JarFile(jarFile)), false); 44 index.indexJar(new ParsedJar(new JarFile(jarFile)), s -> {});
46 } 45 }
47 46
48 @Test 47 @Test
49 public void obfEntries() { 48 public void obfEntries() {
50 assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, 49 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass,
51 subClass, subsubClass, defaultClass, callerClass)); 50 subClass, subsubClass, defaultClass, callerClass));
52 } 51 }
53 52
@@ -55,7 +54,7 @@ public class TestJarIndexConstructorReferences {
55 @SuppressWarnings("unchecked") 54 @SuppressWarnings("unchecked")
56 public void baseDefault() { 55 public void baseDefault() {
57 MethodEntry source = newMethod(baseClass, "<init>", "()V"); 56 MethodEntry source = newMethod(baseClass, "<init>", "()V");
58 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = index.getMethodsReferencing(source); 57 Collection<EntryReference<MethodEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToMethod(source);
59 assertThat(references, containsInAnyOrder( 58 assertThat(references, containsInAnyOrder(
60 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), 59 newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"),
61 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "()V"), 60 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "()V"),
@@ -67,7 +66,7 @@ public class TestJarIndexConstructorReferences {
67 @SuppressWarnings("unchecked") 66 @SuppressWarnings("unchecked")
68 public void baseInt() { 67 public void baseInt() {
69 MethodEntry source = newMethod(baseClass, "<init>", "(I)V"); 68 MethodEntry source = newMethod(baseClass, "<init>", "(I)V");
70 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 69 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
71 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") 70 newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V")
72 )); 71 ));
73 } 72 }
@@ -76,7 +75,7 @@ public class TestJarIndexConstructorReferences {
76 @SuppressWarnings("unchecked") 75 @SuppressWarnings("unchecked")
77 public void subDefault() { 76 public void subDefault() {
78 MethodEntry source = newMethod(subClass, "<init>", "()V"); 77 MethodEntry source = newMethod(subClass, "<init>", "()V");
79 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 78 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
80 newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"), 79 newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"),
81 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(I)V") 80 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(I)V")
82 )); 81 ));
@@ -86,7 +85,7 @@ public class TestJarIndexConstructorReferences {
86 @SuppressWarnings("unchecked") 85 @SuppressWarnings("unchecked")
87 public void subInt() { 86 public void subInt() {
88 MethodEntry source = newMethod(subClass, "<init>", "(I)V"); 87 MethodEntry source = newMethod(subClass, "<init>", "(I)V");
89 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 88 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
90 newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), 89 newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"),
91 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(II)V"), 90 newBehaviorReferenceByMethod(source, subClass.getName(), "<init>", "(II)V"),
92 newBehaviorReferenceByMethod(source, subsubClass.getName(), "<init>", "(I)V") 91 newBehaviorReferenceByMethod(source, subsubClass.getName(), "<init>", "(I)V")
@@ -97,7 +96,7 @@ public class TestJarIndexConstructorReferences {
97 @SuppressWarnings("unchecked") 96 @SuppressWarnings("unchecked")
98 public void subIntInt() { 97 public void subIntInt() {
99 MethodEntry source = newMethod(subClass, "<init>", "(II)V"); 98 MethodEntry source = newMethod(subClass, "<init>", "(II)V");
100 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 99 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
101 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") 100 newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V")
102 )); 101 ));
103 } 102 }
@@ -105,14 +104,14 @@ public class TestJarIndexConstructorReferences {
105 @Test 104 @Test
106 public void subIntIntInt() { 105 public void subIntIntInt() {
107 MethodEntry source = newMethod(subClass, "<init>", "(III)V"); 106 MethodEntry source = newMethod(subClass, "<init>", "(III)V");
108 assertThat(index.getMethodsReferencing(source), is(empty())); 107 assertThat(index.getReferenceIndex().getReferencesToMethod(source), is(empty()));
109 } 108 }
110 109
111 @Test 110 @Test
112 @SuppressWarnings("unchecked") 111 @SuppressWarnings("unchecked")
113 public void subsubInt() { 112 public void subsubInt() {
114 MethodEntry source = newMethod(subsubClass, "<init>", "(I)V"); 113 MethodEntry source = newMethod(subsubClass, "<init>", "(I)V");
115 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 114 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
116 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") 115 newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V")
117 )); 116 ));
118 } 117 }
@@ -121,7 +120,7 @@ public class TestJarIndexConstructorReferences {
121 @SuppressWarnings("unchecked") 120 @SuppressWarnings("unchecked")
122 public void defaultConstructable() { 121 public void defaultConstructable() {
123 MethodEntry source = newMethod(defaultClass, "<init>", "()V"); 122 MethodEntry source = newMethod(defaultClass, "<init>", "()V");
124 assertThat(index.getMethodsReferencing(source), containsInAnyOrder( 123 assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(
125 newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V") 124 newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V")
126 )); 125 ));
127 } 126 }
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
index 23df1a99..36595a3b 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java
@@ -11,12 +11,22 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.*; 14import cuchaz.enigma.analysis.EntryReference;
15import cuchaz.enigma.mapping.entry.*; 15import cuchaz.enigma.analysis.ParsedJar;
16import cuchaz.enigma.analysis.index.EntryIndex;
17import cuchaz.enigma.analysis.index.InheritanceIndex;
18import cuchaz.enigma.analysis.index.JarIndex;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.IndexEntryResolver;
21import cuchaz.enigma.translation.representation.AccessFlags;
22import cuchaz.enigma.translation.representation.entry.ClassEntry;
23import cuchaz.enigma.translation.representation.entry.FieldEntry;
24import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
25import cuchaz.enigma.translation.representation.entry.MethodEntry;
16import org.junit.Test; 26import org.junit.Test;
27import org.objectweb.asm.Opcodes;
17 28
18import java.util.Collection; 29import java.util.Collection;
19import java.util.Set;
20import java.util.jar.JarFile; 30import java.util.jar.JarFile;
21 31
22import static cuchaz.enigma.TestEntryFactory.*; 32import static cuchaz.enigma.TestEntryFactory.*;
@@ -27,7 +37,6 @@ public class TestJarIndexInheritanceTree {
27 37
28 private JarIndex index; 38 private JarIndex index;
29 39
30 private ClassEntry objectClass = newClass("java/lang/Object");
31 private ClassEntry baseClass = newClass("a"); 40 private ClassEntry baseClass = newClass("a");
32 private ClassEntry subClassA = newClass("b"); 41 private ClassEntry subClassA = newClass("b");
33 private ClassEntry subClassAA = newClass("d"); 42 private ClassEntry subClassAA = newClass("d");
@@ -37,13 +46,13 @@ public class TestJarIndexInheritanceTree {
37 46
38 public TestJarIndexInheritanceTree() 47 public TestJarIndexInheritanceTree()
39 throws Exception { 48 throws Exception {
40 index = new JarIndex(new ReferencedEntryPool()); 49 index = JarIndex.empty();
41 index.indexJar(new ParsedJar(new JarFile("build/test-obf/inheritanceTree.jar")), false); 50 index.indexJar(new ParsedJar(new JarFile("build/test-obf/inheritanceTree.jar")), s -> {});
42 } 51 }
43 52
44 @Test 53 @Test
45 public void obfEntries() { 54 public void obfEntries() {
46 assertThat(index.getObfClassEntries(), containsInAnyOrder( 55 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(
47 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB 56 newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB
48 )); 57 ));
49 } 58 }
@@ -51,67 +60,68 @@ public class TestJarIndexInheritanceTree {
51 @Test 60 @Test
52 public void translationIndex() { 61 public void translationIndex() {
53 62
54 TranslationIndex index = this.index.getTranslationIndex(); 63 InheritanceIndex index = this.index.getInheritanceIndex();
55 64
56 // base class 65 // base class
57 assertThat(index.getSuperclass(baseClass), is(objectClass)); 66 assertThat(index.getParents(baseClass), is(empty()));
58 assertThat(index.getAncestry(baseClass), contains(objectClass)); 67 assertThat(index.getAncestors(baseClass), is(empty()));
59 assertThat(index.getSubclass(baseClass), containsInAnyOrder(subClassA, subClassB 68 assertThat(index.getChildren(baseClass), containsInAnyOrder(subClassA, subClassB
60 )); 69 ));
61 70
62 // subclass a 71 // subclass a
63 assertThat(index.getSuperclass(subClassA), is(baseClass)); 72 assertThat(index.getParents(subClassA), contains(baseClass));
64 assertThat(index.getAncestry(subClassA), contains(baseClass, objectClass)); 73 assertThat(index.getAncestors(subClassA), containsInAnyOrder(baseClass));
65 assertThat(index.getSubclass(subClassA), contains(subClassAA)); 74 assertThat(index.getChildren(subClassA), contains(subClassAA));
66 75
67 // subclass aa 76 // subclass aa
68 assertThat(index.getSuperclass(subClassAA), is(subClassA)); 77 assertThat(index.getParents(subClassAA), contains(subClassA));
69 assertThat(index.getAncestry(subClassAA), contains(subClassA, baseClass, objectClass)); 78 assertThat(index.getAncestors(subClassAA), containsInAnyOrder(subClassA, baseClass));
70 assertThat(index.getSubclass(subClassAA), is(empty())); 79 assertThat(index.getChildren(subClassAA), is(empty()));
71 80
72 // subclass b 81 // subclass b
73 assertThat(index.getSuperclass(subClassB), is(baseClass)); 82 assertThat(index.getParents(subClassB), contains(baseClass));
74 assertThat(index.getAncestry(subClassB), contains(baseClass, objectClass)); 83 assertThat(index.getAncestors(subClassB), containsInAnyOrder(baseClass));
75 assertThat(index.getSubclass(subClassB), is(empty())); 84 assertThat(index.getChildren(subClassB), is(empty()));
76 } 85 }
77 86
78 @Test 87 @Test
79 public void access() { 88 public void access() {
80 assertThat(index.getAccess(nameField), is(Access.PRIVATE)); 89 assertThat(index.getEntryIndex().getFieldAccess(nameField), is(new AccessFlags(Opcodes.ACC_PRIVATE)));
81 assertThat(index.getAccess(numThingsField), is(Access.PRIVATE)); 90 assertThat(index.getEntryIndex().getFieldAccess(numThingsField), is(new AccessFlags(Opcodes.ACC_PRIVATE)));
82 } 91 }
83 92
84 @Test 93 @Test
85 public void relatedMethodImplementations() { 94 public void relatedMethodImplementations() {
86 95
87 Set<MethodEntry> entries; 96 Collection<MethodEntry> entries;
88 97
98 EntryResolver resolver = new IndexEntryResolver(index);
89 // getName() 99 // getName()
90 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;")); 100 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()Ljava/lang/String;"));
91 assertThat(entries, containsInAnyOrder( 101 assertThat(entries, containsInAnyOrder(
92 newMethod(baseClass, "a", "()Ljava/lang/String;"), 102 newMethod(baseClass, "a", "()Ljava/lang/String;"),
93 newMethod(subClassAA, "a", "()Ljava/lang/String;") 103 newMethod(subClassAA, "a", "()Ljava/lang/String;")
94 )); 104 ));
95 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()Ljava/lang/String;")); 105 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()Ljava/lang/String;"));
96 assertThat(entries, containsInAnyOrder( 106 assertThat(entries, containsInAnyOrder(
97 newMethod(baseClass, "a", "()Ljava/lang/String;"), 107 newMethod(baseClass, "a", "()Ljava/lang/String;"),
98 newMethod(subClassAA, "a", "()Ljava/lang/String;") 108 newMethod(subClassAA, "a", "()Ljava/lang/String;")
99 )); 109 ));
100 110
101 // doBaseThings() 111 // doBaseThings()
102 entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V")); 112 entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()V"));
103 assertThat(entries, containsInAnyOrder( 113 assertThat(entries, containsInAnyOrder(
104 newMethod(baseClass, "a", "()V"), 114 newMethod(baseClass, "a", "()V"),
105 newMethod(subClassAA, "a", "()V"), 115 newMethod(subClassAA, "a", "()V"),
106 newMethod(subClassB, "a", "()V") 116 newMethod(subClassB, "a", "()V")
107 )); 117 ));
108 entries = index.getRelatedMethodImplementations(newMethod(subClassAA, "a", "()V")); 118 entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()V"));
109 assertThat(entries, containsInAnyOrder( 119 assertThat(entries, containsInAnyOrder(
110 newMethod(baseClass, "a", "()V"), 120 newMethod(baseClass, "a", "()V"),
111 newMethod(subClassAA, "a", "()V"), 121 newMethod(subClassAA, "a", "()V"),
112 newMethod(subClassB, "a", "()V") 122 newMethod(subClassB, "a", "()V")
113 )); 123 ));
114 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "a", "()V")); 124 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "a", "()V"));
115 assertThat(entries, containsInAnyOrder( 125 assertThat(entries, containsInAnyOrder(
116 newMethod(baseClass, "a", "()V"), 126 newMethod(baseClass, "a", "()V"),
117 newMethod(subClassAA, "a", "()V"), 127 newMethod(subClassAA, "a", "()V"),
@@ -119,7 +129,7 @@ public class TestJarIndexInheritanceTree {
119 )); 129 ));
120 130
121 // doBThings 131 // doBThings
122 entries = index.getRelatedMethodImplementations(newMethod(subClassB, "b", "()V")); 132 entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "b", "()V"));
123 assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V"))); 133 assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V")));
124 } 134 }
125 135
@@ -129,14 +139,14 @@ public class TestJarIndexInheritanceTree {
129 Collection<EntryReference<FieldEntry, MethodDefEntry>> references; 139 Collection<EntryReference<FieldEntry, MethodDefEntry>> references;
130 140
131 // name 141 // name
132 references = index.getFieldReferences(nameField); 142 references = index.getReferenceIndex().getReferencesToField(nameField);
133 assertThat(references, containsInAnyOrder( 143 assertThat(references, containsInAnyOrder(
134 newFieldReferenceByMethod(nameField, baseClass.getName(), "<init>", "(Ljava/lang/String;)V"), 144 newFieldReferenceByMethod(nameField, baseClass.getName(), "<init>", "(Ljava/lang/String;)V"),
135 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") 145 newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;")
136 )); 146 ));
137 147
138 // numThings 148 // numThings
139 references = index.getFieldReferences(numThingsField); 149 references = index.getReferenceIndex().getReferencesToField(numThingsField);
140 assertThat(references, containsInAnyOrder( 150 assertThat(references, containsInAnyOrder(
141 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "<init>", "()V"), 151 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "<init>", "()V"),
142 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") 152 newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V")
@@ -152,7 +162,7 @@ public class TestJarIndexInheritanceTree {
152 162
153 // baseClass constructor 163 // baseClass constructor
154 source = newMethod(baseClass, "<init>", "(Ljava/lang/String;)V"); 164 source = newMethod(baseClass, "<init>", "(Ljava/lang/String;)V");
155 references = index.getMethodsReferencing(source); 165 references = index.getReferenceIndex().getReferencesToMethod(source);
156 assertThat(references, containsInAnyOrder( 166 assertThat(references, containsInAnyOrder(
157 newBehaviorReferenceByMethod(source, subClassA.getName(), "<init>", "(Ljava/lang/String;)V"), 167 newBehaviorReferenceByMethod(source, subClassA.getName(), "<init>", "(Ljava/lang/String;)V"),
158 newBehaviorReferenceByMethod(source, subClassB.getName(), "<init>", "()V") 168 newBehaviorReferenceByMethod(source, subClassB.getName(), "<init>", "()V")
@@ -160,14 +170,14 @@ public class TestJarIndexInheritanceTree {
160 170
161 // subClassA constructor 171 // subClassA constructor
162 source = newMethod(subClassA, "<init>", "(Ljava/lang/String;)V"); 172 source = newMethod(subClassA, "<init>", "(Ljava/lang/String;)V");
163 references = index.getMethodsReferencing(source); 173 references = index.getReferenceIndex().getReferencesToMethod(source);
164 assertThat(references, containsInAnyOrder( 174 assertThat(references, containsInAnyOrder(
165 newBehaviorReferenceByMethod(source, subClassAA.getName(), "<init>", "()V") 175 newBehaviorReferenceByMethod(source, subClassAA.getName(), "<init>", "()V")
166 )); 176 ));
167 177
168 // baseClass.getName() 178 // baseClass.getName()
169 source = newMethod(baseClass, "a", "()Ljava/lang/String;"); 179 source = newMethod(baseClass, "a", "()Ljava/lang/String;");
170 references = index.getMethodsReferencing(source); 180 references = index.getReferenceIndex().getReferencesToMethod(source);
171 assertThat(references, containsInAnyOrder( 181 assertThat(references, containsInAnyOrder(
172 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), 182 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"),
173 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") 183 newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V")
@@ -175,7 +185,7 @@ public class TestJarIndexInheritanceTree {
175 185
176 // subclassAA.getName() 186 // subclassAA.getName()
177 source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); 187 source = newMethod(subClassAA, "a", "()Ljava/lang/String;");
178 references = index.getMethodsReferencing(source); 188 references = index.getReferenceIndex().getReferencesToMethod(source);
179 assertThat(references, containsInAnyOrder( 189 assertThat(references, containsInAnyOrder(
180 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") 190 newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V")
181 )); 191 ));
@@ -183,35 +193,35 @@ public class TestJarIndexInheritanceTree {
183 193
184 @Test 194 @Test
185 public void containsEntries() { 195 public void containsEntries() {
186 196 EntryIndex entryIndex = index.getEntryIndex();
187 // classes 197 // classes
188 assertThat(index.containsObfClass(baseClass), is(true)); 198 assertThat(entryIndex.hasClass(baseClass), is(true));
189 assertThat(index.containsObfClass(subClassA), is(true)); 199 assertThat(entryIndex.hasClass(subClassA), is(true));
190 assertThat(index.containsObfClass(subClassAA), is(true)); 200 assertThat(entryIndex.hasClass(subClassAA), is(true));
191 assertThat(index.containsObfClass(subClassB), is(true)); 201 assertThat(entryIndex.hasClass(subClassB), is(true));
192 202
193 // fields 203 // fields
194 assertThat(index.containsObfField(nameField), is(true)); 204 assertThat(entryIndex.hasField(nameField), is(true));
195 assertThat(index.containsObfField(numThingsField), is(true)); 205 assertThat(entryIndex.hasField(numThingsField), is(true));
196 206
197 // methods 207 // methods
198 // getName() 208 // getName()
199 assertThat(index.containsObfMethod(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true)); 209 assertThat(entryIndex.hasMethod(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true));
200 assertThat(index.containsObfMethod(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false)); 210 assertThat(entryIndex.hasMethod(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false));
201 assertThat(index.containsObfMethod(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true)); 211 assertThat(entryIndex.hasMethod(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true));
202 assertThat(index.containsObfMethod(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false)); 212 assertThat(entryIndex.hasMethod(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false));
203 213
204 // doBaseThings() 214 // doBaseThings()
205 assertThat(index.containsObfMethod(newMethod(baseClass, "a", "()V")), is(true)); 215 assertThat(entryIndex.hasMethod(newMethod(baseClass, "a", "()V")), is(true));
206 assertThat(index.containsObfMethod(newMethod(subClassA, "a", "()V")), is(false)); 216 assertThat(entryIndex.hasMethod(newMethod(subClassA, "a", "()V")), is(false));
207 assertThat(index.containsObfMethod(newMethod(subClassAA, "a", "()V")), is(true)); 217 assertThat(entryIndex.hasMethod(newMethod(subClassAA, "a", "()V")), is(true));
208 assertThat(index.containsObfMethod(newMethod(subClassB, "a", "()V")), is(true)); 218 assertThat(entryIndex.hasMethod(newMethod(subClassB, "a", "()V")), is(true));
209 219
210 // doBThings() 220 // doBThings()
211 assertThat(index.containsObfMethod(newMethod(baseClass, "b", "()V")), is(false)); 221 assertThat(entryIndex.hasMethod(newMethod(baseClass, "b", "()V")), is(false));
212 assertThat(index.containsObfMethod(newMethod(subClassA, "b", "()V")), is(false)); 222 assertThat(entryIndex.hasMethod(newMethod(subClassA, "b", "()V")), is(false));
213 assertThat(index.containsObfMethod(newMethod(subClassAA, "b", "()V")), is(false)); 223 assertThat(entryIndex.hasMethod(newMethod(subClassAA, "b", "()V")), is(false));
214 assertThat(index.containsObfMethod(newMethod(subClassB, "b", "()V")), is(true)); 224 assertThat(entryIndex.hasMethod(newMethod(subClassB, "b", "()V")), is(true));
215 225
216 } 226 }
217} 227}
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
index b4529ddc..1299bccc 100644
--- a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -12,12 +12,19 @@
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.analysis.*; 14import cuchaz.enigma.analysis.*;
15import cuchaz.enigma.mapping.*; 15import cuchaz.enigma.analysis.index.EntryIndex;
16import cuchaz.enigma.mapping.entry.*; 16import cuchaz.enigma.analysis.index.InheritanceIndex;
17import cuchaz.enigma.analysis.index.JarIndex;
18import cuchaz.enigma.translation.VoidTranslator;
19import cuchaz.enigma.translation.representation.AccessFlags;
20import cuchaz.enigma.translation.representation.entry.ClassEntry;
21import cuchaz.enigma.translation.representation.entry.FieldEntry;
22import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
23import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import org.junit.Test; 24import org.junit.Test;
18 25
19import java.util.Collection; 26import java.util.Collection;
20import java.util.Set; 27import java.util.List;
21import java.util.jar.JarFile; 28import java.util.jar.JarFile;
22 29
23import static cuchaz.enigma.TestEntryFactory.*; 30import static cuchaz.enigma.TestEntryFactory.*;
@@ -30,13 +37,13 @@ public class TestJarIndexLoneClass {
30 37
31 public TestJarIndexLoneClass() 38 public TestJarIndexLoneClass()
32 throws Exception { 39 throws Exception {
33 index = new JarIndex(new ReferencedEntryPool()); 40 index = JarIndex.empty();
34 index.indexJar(new ParsedJar(new JarFile("build/test-obf/loneClass.jar")), false); 41 index.indexJar(new ParsedJar(new JarFile("build/test-obf/loneClass.jar")), s -> {});
35 } 42 }
36 43
37 @Test 44 @Test
38 public void obfEntries() { 45 public void obfEntries() {
39 assertThat(index.getObfClassEntries(), containsInAnyOrder( 46 assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(
40 newClass("cuchaz/enigma/inputs/Keep"), 47 newClass("cuchaz/enigma/inputs/Keep"),
41 newClass("a") 48 newClass("a")
42 )); 49 ));
@@ -44,25 +51,28 @@ public class TestJarIndexLoneClass {
44 51
45 @Test 52 @Test
46 public void translationIndex() { 53 public void translationIndex() {
47 assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("a")), is(new ClassEntry("java/lang/Object"))); 54 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
48 assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(new ClassEntry("java/lang/Object"))); 55 assertThat(inheritanceIndex.getParents(new ClassEntry("a")), is(empty()));
49 assertThat(index.getTranslationIndex().getAncestry(new ClassEntry("a")), contains(new ClassEntry("java/lang/Object"))); 56 assertThat(inheritanceIndex.getParents(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
50 assertThat(index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), contains(new ClassEntry("java/lang/Object"))); 57 assertThat(inheritanceIndex.getAncestors(new ClassEntry("a")), is(empty()));
51 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("a")), is(empty())); 58 assertThat(inheritanceIndex.getAncestors(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
52 assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); 59 assertThat(inheritanceIndex.getChildren(new ClassEntry("a")), is(empty()));
60 assertThat(inheritanceIndex.getChildren(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
53 } 61 }
54 62
55 @Test 63 @Test
56 public void access() { 64 public void access() {
57 assertThat(index.getAccess(newField("a", "a", "Ljava/lang/String;")), is(Access.PRIVATE)); 65 EntryIndex entryIndex = index.getEntryIndex();
58 assertThat(index.getAccess(newMethod("a", "a", "()Ljava/lang/String;")), is(Access.PUBLIC)); 66 assertThat(entryIndex.getFieldAccess(newField("a", "a", "Ljava/lang/String;")), is(AccessFlags.PRIVATE));
59 assertThat(index.getAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue())); 67 assertThat(entryIndex.getMethodAccess(newMethod("a", "a", "()Ljava/lang/String;")), is(AccessFlags.PUBLIC));
60 assertThat(index.getAccess(newField("a", "a", "LFoo;")), is(nullValue())); 68 assertThat(entryIndex.getFieldAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue()));
69 assertThat(entryIndex.getFieldAccess(newField("a", "a", "LFoo;")), is(nullValue()));
61 } 70 }
62 71
63 @Test 72 @Test
64 public void classInheritance() { 73 public void classInheritance() {
65 ClassInheritanceTreeNode node = index.getClassInheritance(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a")); 74 IndexTreeBuilder treeBuilder = new IndexTreeBuilder(index);
75 ClassInheritanceTreeNode node = treeBuilder.buildClassInheritance(VoidTranslator.INSTANCE, newClass("a"));
66 assertThat(node, is(not(nullValue()))); 76 assertThat(node, is(not(nullValue())));
67 assertThat(node.getObfClassName(), is("a")); 77 assertThat(node.getObfClassName(), is("a"));
68 assertThat(node.getChildCount(), is(0)); 78 assertThat(node.getChildCount(), is(0));
@@ -70,8 +80,9 @@ public class TestJarIndexLoneClass {
70 80
71 @Test 81 @Test
72 public void methodInheritance() { 82 public void methodInheritance() {
83 IndexTreeBuilder treeBuilder = new IndexTreeBuilder(index);
73 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 84 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
74 MethodInheritanceTreeNode node = index.getMethodInheritance(new DirectionalTranslator(new ReferencedEntryPool()), source); 85 MethodInheritanceTreeNode node = treeBuilder.buildMethodInheritance(VoidTranslator.INSTANCE, source);
75 assertThat(node, is(not(nullValue()))); 86 assertThat(node, is(not(nullValue())));
76 assertThat(node.getMethodEntry(), is(source)); 87 assertThat(node.getMethodEntry(), is(source));
77 assertThat(node.getChildCount(), is(0)); 88 assertThat(node.getChildCount(), is(0));
@@ -79,19 +90,24 @@ public class TestJarIndexLoneClass {
79 90
80 @Test 91 @Test
81 public void classImplementations() { 92 public void classImplementations() {
82 ClassImplementationsTreeNode node = index.getClassImplementations(new DirectionalTranslator(new ReferencedEntryPool()), newClass("a")); 93 IndexTreeBuilder treeBuilder = new IndexTreeBuilder(index);
94 ClassImplementationsTreeNode node = treeBuilder.buildClassImplementations(VoidTranslator.INSTANCE, newClass("a"));
83 assertThat(node, is(nullValue())); 95 assertThat(node, is(nullValue()));
84 } 96 }
85 97
86 @Test 98 @Test
87 public void methodImplementations() { 99 public void methodImplementations() {
100 IndexTreeBuilder treeBuilder = new IndexTreeBuilder(index);
88 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); 101 MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;");
89 assertThat(index.getMethodImplementations(new DirectionalTranslator(new ReferencedEntryPool()), source), is(empty())); 102
103 List<MethodImplementationsTreeNode> nodes = treeBuilder.buildMethodImplementations(VoidTranslator.INSTANCE, source);
104 assertThat(nodes, hasSize(1));
105 assertThat(nodes.get(0).getMethodEntry(), is(source));
90 } 106 }
91 107
92 @Test 108 @Test
93 public void relatedMethodImplementations() { 109 public void relatedMethodImplementations() {
94 Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); 110 Collection<MethodEntry> entries = index.getEntryResolver().resolveEquivalentMethods(newMethod("a", "a", "()Ljava/lang/String;"));
95 assertThat(entries, containsInAnyOrder( 111 assertThat(entries, containsInAnyOrder(
96 newMethod("a", "a", "()Ljava/lang/String;") 112 newMethod("a", "a", "()Ljava/lang/String;")
97 )); 113 ));
@@ -101,7 +117,7 @@ public class TestJarIndexLoneClass {
101 @SuppressWarnings("unchecked") 117 @SuppressWarnings("unchecked")
102 public void fieldReferences() { 118 public void fieldReferences() {
103 FieldEntry source = newField("a", "a", "Ljava/lang/String;"); 119 FieldEntry source = newField("a", "a", "Ljava/lang/String;");
104 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getFieldReferences(source); 120 Collection<EntryReference<FieldEntry, MethodDefEntry>> references = index.getReferenceIndex().getReferencesToField(source);
105 assertThat(references, containsInAnyOrder( 121 assertThat(references, containsInAnyOrder(
106 newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"), 122 newFieldReferenceByMethod(source, "a", "<init>", "(Ljava/lang/String;)V"),
107 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") 123 newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;")
@@ -110,42 +126,33 @@ public class TestJarIndexLoneClass {
110 126
111 @Test 127 @Test
112 public void behaviorReferences() { 128 public void behaviorReferences() {
113 assertThat(index.getMethodsReferencing(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); 129 assertThat(index.getReferenceIndex().getReferencesToMethod(newMethod("a", "a", "()Ljava/lang/String;")), is(empty()));
114 }
115
116 @Test
117 public void innerClasses() {
118 assertThat(index.getInnerClasses(newClass("a")), is(empty()));
119 }
120
121 @Test
122 public void outerClass() {
123 assertThat(index.getOuterClass(newClass("a")), is(nullValue()));
124 } 130 }
125 131
126 @Test 132 @Test
127 public void interfaces() { 133 public void interfaces() {
128 assertThat(index.getInterfaces("a"), is(empty())); 134 assertThat(index.getInheritanceIndex().getParents(new ClassEntry("a")), is(empty()));
129 } 135 }
130 136
131 @Test 137 @Test
132 public void implementingClasses() { 138 public void implementingClasses() {
133 assertThat(index.getImplementingClasses("a"), is(empty())); 139 assertThat(index.getInheritanceIndex().getChildren(new ClassEntry("a")), is(empty()));
134 } 140 }
135 141
136 @Test 142 @Test
137 public void isInterface() { 143 public void isInterface() {
138 assertThat(index.isInterface("a"), is(false)); 144 assertThat(index.getInheritanceIndex().isParent(new ClassEntry("a")), is(false));
139 } 145 }
140 146
141 @Test 147 @Test
142 public void testContains() { 148 public void testContains() {
143 assertThat(index.containsObfClass(newClass("a")), is(true)); 149 EntryIndex entryIndex = index.getEntryIndex();
144 assertThat(index.containsObfClass(newClass("b")), is(false)); 150 assertThat(entryIndex.hasClass(newClass("a")), is(true));
145 assertThat(index.containsObfField(newField("a", "a", "Ljava/lang/String;")), is(true)); 151 assertThat(entryIndex.hasClass(newClass("b")), is(false));
146 assertThat(index.containsObfField(newField("a", "b", "Ljava/lang/String;")), is(false)); 152 assertThat(entryIndex.hasField(newField("a", "a", "Ljava/lang/String;")), is(true));
147 assertThat(index.containsObfField(newField("a", "a", "LFoo;")), is(false)); 153 assertThat(entryIndex.hasField(newField("a", "b", "Ljava/lang/String;")), is(false));
148 assertThat(index.containsObfMethod(newMethod("a", "a", "()Ljava/lang/String;")), is(true)); 154 assertThat(entryIndex.hasField(newField("a", "a", "LFoo;")), is(false));
149 assertThat(index.containsObfMethod(newMethod("a", "b", "()Ljava/lang/String;")), is(false)); 155 assertThat(entryIndex.hasMethod(newMethod("a", "a", "()Ljava/lang/String;")), is(true));
156 assertThat(entryIndex.hasMethod(newMethod("a", "b", "()Ljava/lang/String;")), is(false));
150 } 157 }
151} 158}
diff --git a/src/test/java/cuchaz/enigma/TestMethodDescriptor.java b/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
index 48c46e52..a73880dd 100644
--- a/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
+++ b/src/test/java/cuchaz/enigma/TestMethodDescriptor.java
@@ -11,8 +11,8 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.MethodDescriptor; 14import cuchaz.enigma.translation.representation.MethodDescriptor;
15import cuchaz.enigma.mapping.TypeDescriptor; 15import cuchaz.enigma.translation.representation.TypeDescriptor;
16import org.junit.Test; 16import org.junit.Test;
17 17
18import static org.hamcrest.MatcherAssert.assertThat; 18import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java
index 07542753..a5f5f712 100644
--- a/src/test/java/cuchaz/enigma/TestSourceIndex.java
+++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java
@@ -13,7 +13,7 @@ package cuchaz.enigma;
13 13
14import com.google.common.collect.Sets; 14import com.google.common.collect.Sets;
15import com.strobel.decompiler.languages.java.ast.CompilationUnit; 15import com.strobel.decompiler.languages.java.ast.CompilationUnit;
16import cuchaz.enigma.mapping.entry.ClassEntry; 16import cuchaz.enigma.translation.representation.entry.ClassEntry;
17import org.junit.Test; 17import org.junit.Test;
18 18
19import java.io.File; 19import java.io.File;
@@ -44,7 +44,7 @@ public class TestSourceIndex {
44 44
45 // get all classes that aren't inner classes 45 // get all classes that aren't inner classes
46 Set<ClassEntry> classEntries = Sets.newHashSet(); 46 Set<ClassEntry> classEntries = Sets.newHashSet();
47 for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { 47 for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getEntryIndex().getClasses()) {
48 if (!obfClassEntry.isInnerClass()) { 48 if (!obfClassEntry.isInnerClass()) {
49 classEntries.add(obfClassEntry); 49 classEntries.add(obfClassEntry);
50 } 50 }
diff --git a/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
index 0e98da7f..1ee0bde1 100644
--- a/src/test/java/cuchaz/enigma/TestTokensConstructors.java
+++ b/src/test/java/cuchaz/enigma/TestTokensConstructors.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.entry.MethodEntry; 14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15import org.junit.Test; 15import org.junit.Test;
16 16
17import java.util.jar.JarFile; 17import java.util.jar.JarFile;
diff --git a/src/test/java/cuchaz/enigma/TestTranslator.java b/src/test/java/cuchaz/enigma/TestTranslator.java
index 9b6eb916..b9781297 100644
--- a/src/test/java/cuchaz/enigma/TestTranslator.java
+++ b/src/test/java/cuchaz/enigma/TestTranslator.java
@@ -11,23 +11,14 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.entry.Entry; 14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.mapping.Mappings;
16import cuchaz.enigma.mapping.Translator;
17import org.junit.BeforeClass; 15import org.junit.BeforeClass;
18import org.junit.Test; 16import org.junit.Test;
19 17
20import static cuchaz.enigma.TestEntryFactory.newClass; 18import static cuchaz.enigma.TestEntryFactory.*;
21import static cuchaz.enigma.TestEntryFactory.newField;
22import static cuchaz.enigma.TestEntryFactory.newMethod;
23 19
24public class TestTranslator { 20public class TestTranslator {
25 21
26 private static Deobfuscator deobfuscator;
27 private static Mappings mappings;
28 private static Translator deobfTranslator;
29 private static Translator obfTranslator;
30
31 @BeforeClass 22 @BeforeClass
32 public static void beforeClass() 23 public static void beforeClass()
33 throws Exception { 24 throws Exception {
@@ -147,7 +138,7 @@ public class TestTranslator {
147 assertMapping(newMethod("i$b", "a", "()Ljava/lang/Object;"), newMethod("deobf/I_Generics$B_Generic", "m1", "()Ljava/lang/Object;")); 138 assertMapping(newMethod("i$b", "a", "()Ljava/lang/Object;"), newMethod("deobf/I_Generics$B_Generic", "m1", "()Ljava/lang/Object;"));
148 } 139 }
149 140
150 private void assertMapping(Entry obf, Entry deobf) { 141 private void assertMapping(Entry<?> obf, Entry<?> deobf) {
151 //assertThat(deobfTranslator.translateEntry(obf), is(deobf)); 142 //assertThat(deobfTranslator.translateEntry(obf), is(deobf));
152 //assertThat(obfTranslator.translateEntry(deobf), is(obf)); 143 //assertThat(obfTranslator.translateEntry(deobf), is(obf));
153 144
diff --git a/src/test/java/cuchaz/enigma/TestTypeDescriptor.java b/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
index 8172848c..b9ebe559 100644
--- a/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
+++ b/src/test/java/cuchaz/enigma/TestTypeDescriptor.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma; 12package cuchaz.enigma;
13 13
14import cuchaz.enigma.mapping.TypeDescriptor; 14import cuchaz.enigma.translation.representation.TypeDescriptor;
15import org.junit.Test; 15import org.junit.Test;
16 16
17import static cuchaz.enigma.TestEntryFactory.newClass; 17import static cuchaz.enigma.TestEntryFactory.newClass;
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java
index d863a5ae..aac28662 100644
--- a/src/test/java/cuchaz/enigma/TokenChecker.java
+++ b/src/test/java/cuchaz/enigma/TokenChecker.java
@@ -16,7 +16,7 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit;
16import cuchaz.enigma.analysis.EntryReference; 16import cuchaz.enigma.analysis.EntryReference;
17import cuchaz.enigma.analysis.SourceIndex; 17import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.analysis.Token; 18import cuchaz.enigma.analysis.Token;
19import cuchaz.enigma.mapping.entry.Entry; 19import cuchaz.enigma.translation.representation.entry.Entry;
20 20
21import java.io.IOException; 21import java.io.IOException;
22import java.util.Collection; 22import java.util.Collection;
@@ -32,9 +32,9 @@ public class TokenChecker {
32 deobfuscator = new Deobfuscator(jarFile); 32 deobfuscator = new Deobfuscator(jarFile);
33 } 33 }
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.getClassName()); 37 CompilationUnit tree = deobfuscator.getSourceTree(entry.getContainingClass().getFullName());
38 // DEBUG 38 // DEBUG
39 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); 39 // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null );
40 String source = deobfuscator.getSource(tree); 40 String source = deobfuscator.getSource(tree);
@@ -49,15 +49,15 @@ public class TokenChecker {
49 } 49 }
50 50
51 @SuppressWarnings("unchecked") 51 @SuppressWarnings("unchecked")
52 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry, ? extends Entry> reference) { 52 protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) {
53 // decompile the class 53 // decompile the class
54 CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getClassName()); 54 CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getContainingClass().getFullName());
55 String source = deobfuscator.getSource(tree); 55 String source = deobfuscator.getSource(tree);
56 SourceIndex index = deobfuscator.getSourceIndex(tree, source); 56 SourceIndex index = deobfuscator.getSourceIndex(tree, source);
57 57
58 // get the token values 58 // get the token values
59 List<String> values = Lists.newArrayList(); 59 List<String> values = Lists.newArrayList();
60 for (Token token : index.getReferenceTokens((EntryReference<Entry, Entry>) reference)) { 60 for (Token token : index.getReferenceTokens((EntryReference<Entry<?>, Entry<?>>) reference)) {
61 values.add(source.substring(token.start, token.end)); 61 values.add(source.substring(token.start, token.end));
62 } 62 }
63 return values; 63 return values;