diff options
| author | 2019-01-24 14:48:32 +0200 | |
|---|---|---|
| committer | 2019-01-24 13:48:32 +0100 | |
| commit | 00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch) | |
| tree | 6f9e4c24dbcc6d118fceec56adf7bf9d747a485c /src | |
| parent | mark as 0.13.0-SNAPSHOT for preliminary development (diff) | |
| download | enigma-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
Diffstat (limited to 'src')
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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.Deobfuscator.ProgressListener; | 14 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 15 | import cuchaz.enigma.mapping.Mappings; | 15 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; |
| 16 | import cuchaz.enigma.mapping.MappingsEnigmaReader; | 16 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 17 | 17 | ||
| 18 | import java.io.File; | 18 | import java.io.File; |
| 19 | import java.nio.file.Files; | ||
| 20 | import java.nio.file.Path; | ||
| 21 | import java.nio.file.Paths; | ||
| 22 | import java.util.Locale; | ||
| 19 | import java.util.jar.JarFile; | 23 | import java.util.jar.JarFile; |
| 20 | 24 | ||
| 21 | public class CommandMain { | 25 | public 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 | ||
| 14 | import com.google.common.base.Stopwatch; | 14 | import com.google.common.base.Stopwatch; |
| 15 | import com.google.common.collect.Lists; | 15 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Sets; | ||
| 18 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 19 | import com.strobel.assembler.metadata.MetadataSystem; | 17 | import com.strobel.assembler.metadata.MetadataSystem; |
| 20 | import com.strobel.assembler.metadata.TypeDefinition; | 18 | import com.strobel.assembler.metadata.TypeDefinition; |
| @@ -28,16 +26,17 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit; | |||
| 28 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | 26 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; |
| 29 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | 27 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; |
| 30 | import cuchaz.enigma.analysis.*; | 28 | import cuchaz.enigma.analysis.*; |
| 29 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 31 | import cuchaz.enigma.api.EnigmaPlugin; | 30 | import cuchaz.enigma.api.EnigmaPlugin; |
| 32 | import cuchaz.enigma.mapping.*; | 31 | import cuchaz.enigma.translation.mapping.*; |
| 33 | import cuchaz.enigma.mapping.entry.*; | 32 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| 34 | import cuchaz.enigma.throwables.IllegalNameException; | 33 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 34 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 35 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 36 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 37 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 35 | import cuchaz.enigma.utils.Utils; | 38 | import cuchaz.enigma.utils.Utils; |
| 36 | import oml.ast.transformers.InvalidIdentifierFix; | 39 | import oml.ast.transformers.*; |
| 37 | import oml.ast.transformers.Java8Generics; | ||
| 38 | import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; | ||
| 39 | import oml.ast.transformers.RemoveObjectCasts; | ||
| 40 | import oml.ast.transformers.VarargsFixer; | ||
| 41 | import org.objectweb.asm.ClassWriter; | 40 | import org.objectweb.asm.ClassWriter; |
| 42 | import org.objectweb.asm.tree.ClassNode; | 41 | import org.objectweb.asm.tree.ClassNode; |
| 43 | 42 | ||
| @@ -49,6 +48,7 @@ import java.util.function.Consumer; | |||
| 49 | import java.util.jar.JarEntry; | 48 | import java.util.jar.JarEntry; |
| 50 | import java.util.jar.JarFile; | 49 | import java.util.jar.JarFile; |
| 51 | import java.util.jar.JarOutputStream; | 50 | import java.util.jar.JarOutputStream; |
| 51 | import java.util.stream.Collectors; | ||
| 52 | 52 | ||
| 53 | public class Deobfuscator { | 53 | public 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | 3 | import com.strobel.assembler.metadata.ITypeLoader; |
| 4 | import cuchaz.enigma.mapping.entry.ClassEntry; | 4 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 5 | import org.objectweb.asm.ClassWriter; | 5 | import org.objectweb.asm.ClassWriter; |
| 6 | import org.objectweb.asm.tree.ClassNode; | 6 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.config.Config; | ||
| 15 | import cuchaz.enigma.config.Themes; | ||
| 16 | import cuchaz.enigma.gui.Gui; | 14 | import cuchaz.enigma.gui.Gui; |
| 15 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
| 17 | 16 | ||
| 18 | import javax.swing.*; | ||
| 19 | import java.io.File; | 17 | import java.io.File; |
| 18 | import java.nio.file.Files; | ||
| 19 | import java.nio.file.Path; | ||
| 20 | import java.util.jar.JarFile; | 20 | import java.util.jar.JarFile; |
| 21 | 21 | ||
| 22 | public class Main { | 22 | public 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 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.Buffer; | 3 | import com.strobel.assembler.metadata.Buffer; |
| 4 | import cuchaz.enigma.mapping.entry.ClassEntry; | 4 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 5 | import org.objectweb.asm.ClassWriter; | 5 | import org.objectweb.asm.ClassWriter; |
| 6 | import org.objectweb.asm.tree.ClassNode; | 6 | import 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; | |||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.strobel.assembler.metadata.Buffer; | 15 | import com.strobel.assembler.metadata.Buffer; |
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 17 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.analysis.ParsedJar; | 18 | import cuchaz.enigma.analysis.ParsedJar; |
| 19 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | 19 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; |
| 20 | import cuchaz.enigma.mapping.entry.ClassEntry; | 20 | import cuchaz.enigma.translation.Translator; |
| 21 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | 21 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 22 | import cuchaz.enigma.mapping.Translator; | 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 23 | import org.objectweb.asm.ClassWriter; | 23 | import org.objectweb.asm.ClassWriter; |
| 24 | import org.objectweb.asm.Opcodes; | 24 | import org.objectweb.asm.Opcodes; |
| 25 | import org.objectweb.asm.tree.AbstractInsnNode; | 25 | import 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.bytecode.AccessFlags; | 14 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 15 | 15 | ||
| 16 | import java.lang.reflect.Modifier; | 16 | import 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.JarIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 20 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 21 | import javax.swing.tree.DefaultMutableTreeNode; |
| 22 | import java.util.Collection; | ||
| 20 | import java.util.List; | 23 | import java.util.List; |
| 21 | 24 | ||
| 22 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { | 25 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 16 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 17 | 18 | ||
| 18 | import javax.swing.tree.DefaultMutableTreeNode; | 19 | import javax.swing.tree.DefaultMutableTreeNode; |
| 19 | import java.util.List; | 20 | import java.util.List; |
| 20 | 21 | ||
| 21 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { | 22 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 17 | import cuchaz.enigma.mapping.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.mapping.entry.Entry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 20 | import cuchaz.enigma.mapping.entry.MethodEntry; | 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 21 | 21 | ||
| 22 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 23 | import javax.swing.tree.TreeNode; | 23 | import 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | 14 | import cuchaz.enigma.translation.Translatable; |
| 15 | import cuchaz.enigma.mapping.entry.Entry; | 15 | import cuchaz.enigma.translation.Translator; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 17 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import cuchaz.enigma.utils.Utils; | 22 | import cuchaz.enigma.utils.Utils; |
| 18 | 23 | ||
| 19 | import java.util.Arrays; | 24 | import java.util.Arrays; |
| 20 | import java.util.List; | 25 | import java.util.List; |
| 21 | 26 | ||
| 22 | public class EntryReference<E extends Entry, C extends Entry> { | 27 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.mapping.*; | ||
| 18 | import cuchaz.enigma.mapping.entry.*; | ||
| 19 | |||
| 20 | import java.util.AbstractMap; | ||
| 21 | import java.util.List; | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.bytecode.AccessFlags; | 14 | import cuchaz.enigma.analysis.index.JarIndex; |
| 15 | import cuchaz.enigma.mapping.*; | 15 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 16 | import cuchaz.enigma.mapping.entry.FieldEntry; | 16 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 17 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 18 | import cuchaz.enigma.mapping.entry.MethodEntry; | 18 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 19 | 20 | ||
| 20 | import javax.swing.tree.DefaultMutableTreeNode; | 21 | import javax.swing.tree.DefaultMutableTreeNode; |
| 21 | 22 | ||
| 22 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, MethodDefEntry> { | 23 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 4 | import org.objectweb.asm.ClassVisitor; | ||
| 5 | |||
| 6 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 4 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.mapping.Signature; | ||
| 6 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | ||
| 8 | import org.objectweb.asm.ClassVisitor; | ||
| 9 | import org.objectweb.asm.Handle; | ||
| 10 | import org.objectweb.asm.MethodVisitor; | ||
| 11 | import org.objectweb.asm.Opcodes; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.google.common.collect.Lists; | ||
| 4 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 5 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | |||
| 11 | import java.util.List; | ||
| 12 | |||
| 13 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.collect.*; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.*; | ||
| 17 | import cuchaz.enigma.mapping.entry.*; | ||
| 18 | import org.objectweb.asm.ClassReader; | ||
| 19 | import org.objectweb.asm.ClassVisitor; | ||
| 20 | import org.objectweb.asm.Opcodes; | ||
| 21 | |||
| 22 | import java.util.*; | ||
| 23 | |||
| 24 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.translation.Translator; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 21 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 23 | import java.util.Collection; | ||
| 20 | import java.util.List; | 24 | import java.util.List; |
| 21 | 25 | ||
| 22 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { | 26 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.mapping.entry.MethodEntry; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.translation.Translator; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | 21 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | 22 | import javax.swing.tree.DefaultMutableTreeNode; |
| 20 | import java.util.List; | 23 | import java.util.List; |
| 21 | 24 | ||
| 22 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | 25 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 16 | import cuchaz.enigma.mapping.*; | 16 | import cuchaz.enigma.analysis.index.ReferenceIndex; |
| 17 | import cuchaz.enigma.mapping.entry.Entry; | 17 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 18 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 19 | import cuchaz.enigma.mapping.entry.MethodEntry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 20 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 20 | 22 | ||
| 21 | import javax.swing.tree.DefaultMutableTreeNode; | 23 | import javax.swing.tree.DefaultMutableTreeNode; |
| 22 | import javax.swing.tree.TreeNode; | 24 | import javax.swing.tree.TreeNode; |
| 25 | import java.util.ArrayList; | ||
| 26 | import java.util.Collection; | ||
| 23 | import java.util.Set; | 27 | import java.util.Set; |
| 24 | 28 | ||
| 25 | public class MethodReferenceTreeNode extends DefaultMutableTreeNode | 29 | public 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 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.io.ByteStreams; | 14 | import com.google.common.io.ByteStreams; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 16 | import org.objectweb.asm.ClassReader; | 16 | import org.objectweb.asm.ClassReader; |
| 17 | import org.objectweb.asm.ClassVisitor; | 17 | import org.objectweb.asm.ClassVisitor; |
| 18 | import org.objectweb.asm.tree.ClassNode; | 18 | import org.objectweb.asm.tree.ClassNode; |
| 19 | 19 | ||
| 20 | import javax.annotation.Nullable; | ||
| 20 | import java.io.BufferedInputStream; | 21 | import java.io.BufferedInputStream; |
| 21 | import java.io.IOException; | 22 | import java.io.IOException; |
| 22 | import java.io.InputStream; | 23 | import 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.Entry; | 14 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 15 | 15 | ||
| 16 | public interface ReferenceTreeNode<E extends Entry, C extends Entry> { | 16 | public 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 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.*; |
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Multimap; | ||
| 18 | import com.strobel.decompiler.languages.Region; | 15 | import com.strobel.decompiler.languages.Region; |
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | 16 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 20 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | 17 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; |
| 21 | import com.strobel.decompiler.languages.java.ast.Identifier; | 18 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 22 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | 19 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; |
| 23 | import cuchaz.enigma.mapping.entry.Entry; | 20 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 24 | 21 | ||
| 22 | import javax.annotation.Nullable; | ||
| 25 | import java.util.*; | 23 | import java.util.*; |
| 26 | import java.util.regex.Pattern; | 24 | import 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; | |||
| 17 | import com.strobel.assembler.metadata.TypeReference; | 17 | import com.strobel.assembler.metadata.TypeReference; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.bytecode.AccessFlags; | 20 | import cuchaz.enigma.translation.representation.ProcyonEntryFactory; |
| 21 | import cuchaz.enigma.mapping.Signature; | 21 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 22 | import cuchaz.enigma.mapping.entry.*; | 22 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 23 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 24 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 25 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 23 | 26 | ||
| 24 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 27 | public 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.*; | |||
| 17 | import com.strobel.decompiler.ast.Variable; | 17 | import com.strobel.decompiler.ast.Variable; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.mapping.TypeDescriptor; | 20 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 21 | import cuchaz.enigma.mapping.entry.*; | 21 | import cuchaz.enigma.translation.representation.*; |
| 22 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 22 | 23 | ||
| 23 | import java.lang.Error; | 24 | import java.lang.Error; |
| 24 | import java.util.HashMap; | 25 | import java.util.HashMap; |
| @@ -26,19 +27,15 @@ import java.util.Map; | |||
| 26 | 27 | ||
| 27 | public class SourceIndexMethodVisitor extends SourceIndexVisitor { | 28 | public 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; | |||
| 14 | import com.strobel.assembler.metadata.TypeDefinition; | 14 | import com.strobel.assembler.metadata.TypeDefinition; |
| 15 | import com.strobel.decompiler.languages.java.ast.*; | 15 | import com.strobel.decompiler.languages.java.ast.*; |
| 16 | import com.strobel.decompiler.patterns.Pattern; | 16 | import com.strobel.decompiler.patterns.Pattern; |
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | 17 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 18 | import cuchaz.enigma.mapping.Signature; | 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 19 | import cuchaz.enigma.mapping.entry.ClassDefEntry; | ||
| 20 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | ||
| 21 | 19 | ||
| 22 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { | 20 | public 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 | |||
| 12 | package cuchaz.enigma.analysis; | ||
| 13 | |||
| 14 | import com.google.common.cache.Cache; | ||
| 15 | import com.google.common.cache.CacheBuilder; | ||
| 16 | import com.google.common.collect.HashMultimap; | ||
| 17 | import com.google.common.collect.Lists; | ||
| 18 | import com.google.common.collect.Maps; | ||
| 19 | import com.google.common.collect.Multimap; | ||
| 20 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 21 | import cuchaz.enigma.mapping.*; | ||
| 22 | import cuchaz.enigma.mapping.entry.*; | ||
| 23 | |||
| 24 | import java.util.*; | ||
| 25 | |||
| 26 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 6 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 8 | |||
| 9 | import javax.annotation.Nullable; | ||
| 10 | import java.util.Collection; | ||
| 11 | import java.util.Map; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 6 | |||
| 7 | import javax.annotation.Nullable; | ||
| 8 | import java.util.Collection; | ||
| 9 | import java.util.HashMap; | ||
| 10 | import java.util.Map; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.mapping.entry.ClassDefEntry; | 3 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 4 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 4 | import org.objectweb.asm.ClassVisitor; | 6 | import org.objectweb.asm.ClassVisitor; |
| 5 | import org.objectweb.asm.FieldVisitor; | 7 | import org.objectweb.asm.FieldVisitor; |
| 6 | import org.objectweb.asm.MethodVisitor; | 8 | import org.objectweb.asm.MethodVisitor; |
| 7 | 9 | ||
| 8 | public class IndexClassVisitor extends ClassVisitor { | 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.translation.representation.Signature; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 10 | import org.objectweb.asm.ClassVisitor; | ||
| 11 | import org.objectweb.asm.Handle; | ||
| 12 | import org.objectweb.asm.MethodVisitor; | ||
| 13 | import org.objectweb.asm.Opcodes; | ||
| 14 | |||
| 15 | public 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 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | |||
| 21 | import java.util.Collection; | ||
| 22 | import java.util.LinkedList; | ||
| 23 | import java.util.Set; | ||
| 24 | |||
| 25 | public 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 | |||
| 12 | package cuchaz.enigma.analysis.index; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | import cuchaz.enigma.analysis.ParsedJar; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 19 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 21 | import org.objectweb.asm.ClassReader; | ||
| 22 | import org.objectweb.asm.Opcodes; | ||
| 23 | |||
| 24 | import java.util.Arrays; | ||
| 25 | import java.util.Collection; | ||
| 26 | import java.util.function.Consumer; | ||
| 27 | |||
| 28 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 5 | |||
| 6 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import com.google.common.collect.HashMultimap; | ||
| 4 | import com.google.common.collect.Multimap; | ||
| 5 | import cuchaz.enigma.analysis.EntryReference; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 7 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 9 | |||
| 10 | import java.util.Collection; | ||
| 11 | import java.util.Map; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 8 | import org.objectweb.asm.Handle; | ||
| 9 | import org.objectweb.asm.Type; | ||
| 10 | |||
| 11 | public 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 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | 1 | package cuchaz.enigma.bytecode.translators; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.mapping.Translator; | 3 | import cuchaz.enigma.translation.Translator; |
| 4 | import cuchaz.enigma.mapping.TypeDescriptor; | 4 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 5 | import cuchaz.enigma.mapping.entry.ClassEntry; | 5 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 6 | import cuchaz.enigma.mapping.entry.FieldEntry; | 6 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 7 | import org.objectweb.asm.AnnotationVisitor; | 7 | import org.objectweb.asm.AnnotationVisitor; |
| 8 | 8 | ||
| 9 | public class TranslationAnnotationVisitor extends AnnotationVisitor { | 9 | public 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 | ||
| 12 | package cuchaz.enigma.bytecode.translators; | 12 | package cuchaz.enigma.bytecode.translators; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.JarIndex; | 14 | import cuchaz.enigma.translation.Translator; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 16 | import cuchaz.enigma.mapping.MethodDescriptor; | 16 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; |
| 17 | import cuchaz.enigma.mapping.Signature; | 17 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 18 | import cuchaz.enigma.mapping.Translator; | 18 | import cuchaz.enigma.translation.representation.entry.*; |
| 19 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 20 | import cuchaz.enigma.mapping.entry.*; | ||
| 21 | import org.objectweb.asm.*; | 19 | import org.objectweb.asm.*; |
| 22 | 20 | ||
| 21 | import java.util.Arrays; | ||
| 22 | |||
| 23 | public class TranslationClassVisitor extends ClassVisitor { | 23 | public 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 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | 1 | package cuchaz.enigma.bytecode.translators; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.mapping.Translator; | 3 | import cuchaz.enigma.translation.Translator; |
| 4 | import cuchaz.enigma.mapping.TypeDescriptor; | 4 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 5 | import cuchaz.enigma.mapping.entry.FieldDefEntry; | 5 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; |
| 6 | import org.objectweb.asm.AnnotationVisitor; | 6 | import org.objectweb.asm.AnnotationVisitor; |
| 7 | import org.objectweb.asm.FieldVisitor; | 7 | import org.objectweb.asm.FieldVisitor; |
| 8 | import org.objectweb.asm.TypePath; | 8 | import 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 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | 1 | package cuchaz.enigma.bytecode.translators; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.mapping.*; | 3 | import cuchaz.enigma.translation.Translator; |
| 4 | import cuchaz.enigma.mapping.entry.*; | 4 | import cuchaz.enigma.translation.mapping.NameValidator; |
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.Signature; | ||
| 7 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 5 | import org.objectweb.asm.*; | 9 | import org.objectweb.asm.*; |
| 6 | 10 | ||
| 7 | import java.util.Collection; | 11 | import 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; | |||
| 17 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; | 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; |
| 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; | 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; |
| 20 | import cuchaz.enigma.mapping.entry.ClassEntry; | 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 21 | import cuchaz.enigma.throwables.IllegalNameException; | 21 | import cuchaz.enigma.throwables.IllegalNameException; |
| 22 | 22 | ||
| 23 | import javax.swing.*; | 23 | import javax.swing.*; |
| 24 | import javax.swing.event.CellEditorListener; | 24 | import javax.swing.event.CellEditorListener; |
| 25 | import javax.swing.event.ChangeEvent; | 25 | import javax.swing.event.ChangeEvent; |
| 26 | import javax.swing.tree.*; | 26 | import javax.swing.tree.*; |
| 27 | import java.awt.*; | ||
| 28 | import java.awt.event.MouseAdapter; | 27 | import java.awt.event.MouseAdapter; |
| 29 | import java.awt.event.MouseEvent; | 28 | import java.awt.event.MouseEvent; |
| 30 | import java.util.*; | 29 | import java.util.*; |
| @@ -32,7 +31,7 @@ import java.util.List; | |||
| 32 | 31 | ||
| 33 | public class ClassSelector extends JTree { | 32 | public 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; | |||
| 16 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 17 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.analysis.SourceIndex; |
| 18 | import cuchaz.enigma.analysis.Token; | 18 | import cuchaz.enigma.analysis.Token; |
| 19 | import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; | 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 20 | import cuchaz.enigma.mapping.entry.ClassEntry; | 20 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 21 | import cuchaz.enigma.mapping.entry.Entry; | ||
| 22 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 21 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 23 | 22 | ||
| 24 | import javax.swing.*; | 23 | import 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; | |||
| 29 | import cuchaz.enigma.gui.panels.PanelEditor; | 29 | import cuchaz.enigma.gui.panels.PanelEditor; |
| 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; | 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; |
| 31 | import cuchaz.enigma.gui.panels.PanelObf; | 31 | import cuchaz.enigma.gui.panels.PanelObf; |
| 32 | import cuchaz.enigma.mapping.*; | ||
| 33 | import cuchaz.enigma.mapping.entry.*; | ||
| 34 | import cuchaz.enigma.throwables.IllegalNameException; | 32 | import cuchaz.enigma.throwables.IllegalNameException; |
| 33 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 34 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 35 | import cuchaz.enigma.utils.Utils; | 35 | import cuchaz.enigma.utils.Utils; |
| 36 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 36 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 37 | 37 | ||
| @@ -44,8 +44,8 @@ import javax.swing.tree.TreeNode; | |||
| 44 | import javax.swing.tree.TreePath; | 44 | import javax.swing.tree.TreePath; |
| 45 | import java.awt.*; | 45 | import java.awt.*; |
| 46 | import java.awt.event.*; | 46 | import java.awt.event.*; |
| 47 | import java.io.File; | ||
| 48 | import java.io.IOException; | 47 | import java.io.IOException; |
| 48 | import java.nio.file.Path; | ||
| 49 | import java.util.*; | 49 | import java.util.*; |
| 50 | import java.util.List; | 50 | import java.util.List; |
| 51 | import java.util.function.Function; | 51 | import 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.*; | |||
| 20 | import cuchaz.enigma.api.EnigmaPlugin; | 20 | import cuchaz.enigma.api.EnigmaPlugin; |
| 21 | import cuchaz.enigma.config.Config; | 21 | import cuchaz.enigma.config.Config; |
| 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; | 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; |
| 23 | import cuchaz.enigma.mapping.*; | ||
| 24 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 25 | import cuchaz.enigma.mapping.entry.Entry; | ||
| 26 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 27 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 28 | import cuchaz.enigma.throwables.MappingParseException; | 23 | import cuchaz.enigma.throwables.MappingParseException; |
| 24 | import cuchaz.enigma.translation.Translator; | ||
| 25 | import cuchaz.enigma.translation.mapping.*; | ||
| 26 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
| 27 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 28 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 29 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 30 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 31 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 29 | import cuchaz.enigma.utils.ReadableToken; | 32 | import cuchaz.enigma.utils.ReadableToken; |
| 30 | 33 | ||
| 34 | import javax.annotation.Nullable; | ||
| 31 | import java.awt.event.ItemEvent; | 35 | import java.awt.event.ItemEvent; |
| 32 | import java.io.File; | 36 | import java.io.File; |
| 33 | import java.io.IOException; | 37 | import java.io.IOException; |
| 38 | import java.nio.file.Path; | ||
| 34 | import java.util.*; | 39 | import java.util.*; |
| 35 | import java.util.jar.JarFile; | 40 | import java.util.jar.JarFile; |
| 41 | import java.util.stream.Collectors; | ||
| 36 | 42 | ||
| 37 | public class GuiController { | 43 | public 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 | |||
| 12 | package cuchaz.enigma.gui; | ||
| 13 | |||
| 14 | import javax.swing.*; | ||
| 15 | import java.awt.*; | ||
| 16 | import java.awt.event.ActionListener; | ||
| 17 | |||
| 18 | public 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 | |||
| 12 | package cuchaz.enigma.gui; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | |||
| 16 | public 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 @@ | |||
| 12 | package cuchaz.enigma.gui.dialog; | 12 | package cuchaz.enigma.gui.dialog; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 15 | import cuchaz.enigma.Deobfuscator.ProgressListener; | 15 | import cuchaz.enigma.ProgressListener; |
| 16 | import cuchaz.enigma.utils.Utils; | 16 | import cuchaz.enigma.utils.Utils; |
| 17 | 17 | ||
| 18 | import javax.swing.*; | 18 | import 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; | |||
| 5 | import cuchaz.enigma.gui.Gui; | 5 | import cuchaz.enigma.gui.Gui; |
| 6 | import cuchaz.enigma.gui.dialog.AboutDialog; | 6 | import cuchaz.enigma.gui.dialog.AboutDialog; |
| 7 | import cuchaz.enigma.throwables.MappingParseException; | 7 | import cuchaz.enigma.throwables.MappingParseException; |
| 8 | import cuchaz.enigma.translation.mapping.serde.MappingFormat; | ||
| 8 | 9 | ||
| 9 | import javax.swing.*; | 10 | import javax.swing.*; |
| 10 | import java.awt.event.InputEvent; | 11 | import 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 | ||
| 12 | package cuchaz.enigma.gui.node; | 12 | package cuchaz.enigma.gui.node; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | 14 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 15 | 15 | ||
| 16 | import javax.swing.tree.DefaultMutableTreeNode; | 16 | import 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 | ||
| 3 | import cuchaz.enigma.gui.ClassSelector; | 3 | import cuchaz.enigma.gui.ClassSelector; |
| 4 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 5 | import cuchaz.enigma.mapping.entry.ClassEntry; | 5 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 6 | 6 | ||
| 7 | import javax.swing.*; | 7 | import javax.swing.*; |
| 8 | import java.awt.*; | 8 | import 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Maps; | ||
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 16 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 17 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 19 | |||
| 20 | import java.util.ArrayList; | ||
| 21 | import java.util.Map; | ||
| 22 | |||
| 23 | // FIXME: Enigma doesn't support inner classes of inner class????! | ||
| 24 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 18 | import cuchaz.enigma.mapping.entry.*; | ||
| 19 | |||
| 20 | import java.util.ArrayList; | ||
| 21 | import java.util.List; | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.ServiceLoader; | ||
| 24 | |||
| 25 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 16 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 17 | |||
| 18 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.LocalVariableEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 16 | |||
| 17 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 18 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 19 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 20 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 22 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 23 | |||
| 24 | import java.io.File; | ||
| 25 | import java.io.IOException; | ||
| 26 | import java.util.*; | ||
| 27 | import java.util.stream.Collectors; | ||
| 28 | |||
| 29 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | ||
| 17 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 18 | import cuchaz.enigma.mapping.entry.EntryFactory; | ||
| 19 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 20 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 21 | |||
| 22 | import java.util.Map; | ||
| 23 | |||
| 24 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import com.google.common.collect.Queues; | ||
| 5 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 6 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 7 | |||
| 8 | import java.io.BufferedReader; | ||
| 9 | import java.io.File; | ||
| 10 | import java.io.FileInputStream; | ||
| 11 | import java.io.IOException; | ||
| 12 | import java.io.InputStream; | ||
| 13 | import java.io.InputStreamReader; | ||
| 14 | import java.util.Deque; | ||
| 15 | import java.util.function.Supplier; | ||
| 16 | |||
| 17 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.base.Charsets; | ||
| 15 | |||
| 16 | import java.io.*; | ||
| 17 | import java.util.*; | ||
| 18 | |||
| 19 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.JarIndex; | ||
| 16 | import cuchaz.enigma.mapping.entry.*; | ||
| 17 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 19 | |||
| 20 | import java.io.IOException; | ||
| 21 | import java.io.ObjectOutputStream; | ||
| 22 | import java.io.OutputStream; | ||
| 23 | import java.util.List; | ||
| 24 | import java.util.Set; | ||
| 25 | import java.util.zip.GZIPOutputStream; | ||
| 26 | |||
| 27 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 5 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | ||
| 6 | |||
| 7 | import java.io.*; | ||
| 8 | import java.util.ArrayList; | ||
| 9 | import java.util.Collections; | ||
| 10 | import java.util.List; | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Created by Mark on 11/08/2016. | ||
| 14 | */ | ||
| 15 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import com.google.common.collect.Maps; | ||
| 5 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 6 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 7 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 8 | |||
| 9 | import java.io.File; | ||
| 10 | import java.io.IOException; | ||
| 11 | import java.nio.file.Files; | ||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.List; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.Entry; | ||
| 16 | |||
| 17 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 17 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 18 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 19 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 20 | |||
| 21 | import java.util.Map; | ||
| 22 | |||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.*; | ||
| 15 | import org.objectweb.asm.Handle; | ||
| 16 | import org.objectweb.asm.Type; | ||
| 17 | |||
| 18 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.Signature; | ||
| 17 | |||
| 18 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Lists; | ||
| 16 | |||
| 17 | import java.util.List; | ||
| 18 | |||
| 19 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 4 | |||
| 5 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.mapping.ClassMapping; | ||
| 16 | import cuchaz.enigma.mapping.FieldMapping; | ||
| 17 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 18 | import cuchaz.enigma.mapping.MethodMapping; | ||
| 19 | |||
| 20 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.Signature; | ||
| 17 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 18 | |||
| 19 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 17 | |||
| 18 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 5 | import cuchaz.enigma.utils.Utils; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * TypeDescriptor... | ||
| 9 | * Created by Thog | ||
| 10 | * 19/10/2016 | ||
| 11 | */ | ||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.utils.Utils; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * TypeDescriptor... | ||
| 9 | * Created by Thog | ||
| 10 | * 19/10/2016 | ||
| 11 | */ | ||
| 12 | public 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 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 17 | |||
| 18 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | |||
| 7 | public 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 | ||
| 12 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.translation; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | |||
| 7 | public 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 | ||
| 12 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.translation; |
| 13 | 13 | ||
| 14 | public enum TranslationDirection { | 14 | public 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 | |||
| 12 | package cuchaz.enigma.translation; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | |||
| 17 | import java.util.Collection; | ||
| 18 | import java.util.HashMap; | ||
| 19 | import java.util.Map; | ||
| 20 | import java.util.stream.Collectors; | ||
| 21 | |||
| 22 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.Collection; | ||
| 7 | |||
| 8 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import javax.annotation.Nonnull; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 4 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 5 | import cuchaz.enigma.translation.Translatable; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 12 | |||
| 13 | import javax.annotation.Nullable; | ||
| 14 | import java.util.Collection; | ||
| 15 | |||
| 16 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.common.collect.Streams; | ||
| 4 | import cuchaz.enigma.analysis.EntryReference; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 7 | |||
| 8 | import java.util.Collection; | ||
| 9 | import java.util.Set; | ||
| 10 | import java.util.stream.Collectors; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.common.collect.Sets; | ||
| 4 | import cuchaz.enigma.analysis.IndexTreeBuilder; | ||
| 5 | import cuchaz.enigma.analysis.MethodImplementationsTreeNode; | ||
| 6 | import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | ||
| 7 | import cuchaz.enigma.analysis.index.BridgeMethodIndex; | ||
| 8 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 9 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 10 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 11 | import cuchaz.enigma.translation.VoidTranslator; | ||
| 12 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | |||
| 17 | import javax.annotation.Nullable; | ||
| 18 | import java.util.*; | ||
| 19 | import java.util.stream.Collectors; | ||
| 20 | |||
| 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translatable; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 6 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 8 | |||
| 9 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 7 | |||
| 8 | import java.util.Collection; | ||
| 9 | |||
| 10 | public 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 | |||
| 12 | package cuchaz.enigma.translation.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 15 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 16 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 21 | |||
| 22 | import java.util.Collection; | ||
| 23 | import java.util.HashMap; | ||
| 24 | import java.util.Map; | ||
| 25 | |||
| 26 | public 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 | ||
| 12 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.translation.mapping; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.throwables.IllegalNameException; | 14 | import cuchaz.enigma.throwables.IllegalNameException; |
| 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 16 | 16 | ||
| 17 | import java.util.Arrays; | 17 | import java.util.Arrays; |
| 18 | import java.util.List; | 18 | import java.util.List; |
| 19 | import java.util.regex.Pattern; | 19 | import java.util.regex.Pattern; |
| 20 | 20 | ||
| 21 | public class NameValidator { | 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 5 | |||
| 6 | import java.util.Collection; | ||
| 7 | import java.util.Collections; | ||
| 8 | import java.util.Set; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingPair; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 10 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 13 | |||
| 14 | import javax.annotation.Nullable; | ||
| 15 | import java.io.IOException; | ||
| 16 | import java.nio.file.Files; | ||
| 17 | import java.nio.file.Path; | ||
| 18 | import java.util.ArrayDeque; | ||
| 19 | import java.util.Deque; | ||
| 20 | import java.util.List; | ||
| 21 | import java.util.Locale; | ||
| 22 | import java.util.stream.Collectors; | ||
| 23 | |||
| 24 | public 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 | |||
| 12 | package cuchaz.enigma.translation.mapping.serde; | ||
| 13 | |||
| 14 | import cuchaz.enigma.ProgressListener; | ||
| 15 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 20 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 21 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 22 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 23 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 24 | |||
| 25 | import java.io.IOException; | ||
| 26 | import java.io.PrintWriter; | ||
| 27 | import java.nio.file.DirectoryStream; | ||
| 28 | import java.nio.file.Files; | ||
| 29 | import java.nio.file.Path; | ||
| 30 | import java.nio.file.Paths; | ||
| 31 | import java.util.ArrayList; | ||
| 32 | import java.util.Collection; | ||
| 33 | import java.util.concurrent.atomic.AtomicInteger; | ||
| 34 | import java.util.stream.Collectors; | ||
| 35 | |||
| 36 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 8 | |||
| 9 | import javax.annotation.Nullable; | ||
| 10 | import java.io.IOException; | ||
| 11 | import java.nio.file.Path; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 5 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 6 | |||
| 7 | import java.io.IOException; | ||
| 8 | import java.nio.file.Path; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 5 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 6 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 7 | |||
| 8 | import java.nio.file.Path; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.collect.Lists; | ||
| 4 | import cuchaz.enigma.ProgressListener; | ||
| 5 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 8 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 9 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 11 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | |||
| 17 | import java.io.IOException; | ||
| 18 | import java.io.PrintWriter; | ||
| 19 | import java.nio.file.Files; | ||
| 20 | import java.nio.file.Path; | ||
| 21 | import java.util.ArrayList; | ||
| 22 | import java.util.Collection; | ||
| 23 | import java.util.Comparator; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.stream.Collectors; | ||
| 26 | |||
| 27 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingPair; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 10 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 15 | |||
| 16 | import java.io.IOException; | ||
| 17 | import java.nio.file.Files; | ||
| 18 | import java.nio.file.Path; | ||
| 19 | import java.util.List; | ||
| 20 | |||
| 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 5 | |||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.Iterator; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 5 | |||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.util.Collection; | ||
| 8 | |||
| 9 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.ArrayList; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.stream.Collectors; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.*; | ||
| 7 | import java.util.stream.Collectors; | ||
| 8 | |||
| 9 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.Collection; | ||
| 7 | import java.util.HashMap; | ||
| 8 | import java.util.Iterator; | ||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | public 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 @@ | |||
| 1 | package cuchaz.enigma.bytecode; | 1 | package cuchaz.enigma.translation.representation; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.analysis.Access; | 3 | import cuchaz.enigma.analysis.Access; |
| 4 | import org.objectweb.asm.Opcodes; | 4 | import org.objectweb.asm.Opcodes; |
| @@ -6,6 +6,9 @@ import org.objectweb.asm.Opcodes; | |||
| 6 | import java.lang.reflect.Modifier; | 6 | import java.lang.reflect.Modifier; |
| 7 | 7 | ||
| 8 | public class AccessFlags { | 8 | public 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 | ||
| 12 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.translation.representation; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.translation.Translatable; |
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 16 | import cuchaz.enigma.utils.Utils; | 21 | import cuchaz.enigma.utils.Utils; |
| 17 | 22 | ||
| 18 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
| 19 | import java.util.List; | 24 | import java.util.List; |
| 20 | import java.util.function.Function; | 25 | import java.util.function.Function; |
| 21 | 26 | ||
| 22 | public class MethodDescriptor { | 27 | public 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 | ||
| 12 | package cuchaz.enigma.mapping.entry; | 12 | package cuchaz.enigma.translation.representation; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | 14 | import com.strobel.assembler.metadata.FieldDefinition; |
| 15 | import com.strobel.assembler.metadata.MemberReference; | 15 | import com.strobel.assembler.metadata.MemberReference; |
| 16 | import com.strobel.assembler.metadata.MethodDefinition; | 16 | import com.strobel.assembler.metadata.MethodDefinition; |
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | 17 | import cuchaz.enigma.translation.representation.entry.*; |
| 18 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 19 | import cuchaz.enigma.mapping.Signature; | ||
| 20 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 21 | 18 | ||
| 22 | public class ProcyonEntryFactory { | 19 | public 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 | ||
| 12 | package cuchaz.enigma.mapping.entry; | 12 | package cuchaz.enigma.translation.representation; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.MethodDescriptor; | 14 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 15 | import cuchaz.enigma.mapping.TypeDescriptor; | 15 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 16 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | 17 | ||
| 17 | import java.util.HashMap; | 18 | import java.util.HashMap; |
| 18 | import java.util.Map; | 19 | import 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | 1 | package cuchaz.enigma.translation.representation; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; | 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; |
| 4 | import cuchaz.enigma.translation.Translatable; | ||
| 5 | import cuchaz.enigma.translation.Translator; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 8 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 4 | import org.objectweb.asm.signature.SignatureReader; | 10 | import org.objectweb.asm.signature.SignatureReader; |
| 5 | import org.objectweb.asm.signature.SignatureVisitor; | 11 | import org.objectweb.asm.signature.SignatureVisitor; |
| 6 | import org.objectweb.asm.signature.SignatureWriter; | 12 | import org.objectweb.asm.signature.SignatureWriter; |
| @@ -8,7 +14,7 @@ import org.objectweb.asm.signature.SignatureWriter; | |||
| 8 | import java.util.function.Function; | 14 | import java.util.function.Function; |
| 9 | import java.util.regex.Pattern; | 15 | import java.util.regex.Pattern; |
| 10 | 16 | ||
| 11 | public class Signature { | 17 | public 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 | ||
| 12 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.translation.representation; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | 16 | import cuchaz.enigma.translation.Translatable; |
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 17 | 22 | ||
| 18 | import java.util.Map; | 23 | import java.util.Map; |
| 19 | import java.util.function.Function; | 24 | import java.util.function.Function; |
| 20 | 25 | ||
| 21 | public class TypeDescriptor { | 26 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 18 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 19 | import cuchaz.enigma.translation.representation.Signature; | ||
| 20 | |||
| 21 | import javax.annotation.Nullable; | ||
| 22 | import java.util.Arrays; | ||
| 23 | |||
| 24 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.strobel.assembler.metadata.TypeReference; | ||
| 15 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 18 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | import java.util.List; | ||
| 22 | import java.util.Objects; | ||
| 23 | |||
| 24 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | |||
| 5 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 15 | import cuchaz.enigma.translation.Translatable; | ||
| 16 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 17 | |||
| 18 | import javax.annotation.Nullable; | ||
| 19 | import java.util.ArrayList; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 22 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 18 | import cuchaz.enigma.translation.representation.Signature; | ||
| 19 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 20 | |||
| 21 | import javax.annotation.Nullable; | ||
| 22 | |||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 18 | import cuchaz.enigma.utils.Utils; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | |||
| 22 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | |||
| 8 | import javax.annotation.Nullable; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * TypeDescriptor... | ||
| 12 | * Created by Thog | ||
| 13 | * 19/10/2016 | ||
| 14 | */ | ||
| 15 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.utils.Utils; | ||
| 7 | |||
| 8 | import javax.annotation.Nullable; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * TypeDescriptor... | ||
| 12 | * Created by Thog | ||
| 13 | * 19/10/2016 | ||
| 14 | */ | ||
| 15 | public 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 | ||
| 12 | package cuchaz.enigma.mapping.entry; | 12 | package cuchaz.enigma.translation.representation.entry; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | 15 | import cuchaz.enigma.translation.Translator; |
| 16 | import cuchaz.enigma.mapping.MethodDescriptor; | 16 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 17 | import cuchaz.enigma.mapping.Signature; | 17 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 18 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 19 | import cuchaz.enigma.translation.representation.Signature; | ||
| 18 | 20 | ||
| 19 | public class MethodDefEntry extends MethodEntry implements DefEntry { | 21 | import javax.annotation.Nullable; |
| 20 | 22 | ||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 18 | import cuchaz.enigma.utils.Utils; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | |||
| 22 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translatable; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 21 | |||
| 22 | import javax.annotation.Nullable; | ||
| 23 | |||
| 24 | public 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.analysis.ParsedJar; | 14 | import cuchaz.enigma.analysis.ParsedJar; |
| 16 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 17 | import org.junit.BeforeClass; | 16 | import org.junit.BeforeClass; |
| 18 | import org.junit.Test; | 17 | import 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 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 16 | import org.junit.Test; | 16 | import org.junit.Test; |
| 17 | 17 | ||
| 18 | import java.io.IOException; | 18 | import 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 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 15 | import cuchaz.enigma.mapping.*; | 15 | import cuchaz.enigma.translation.representation.*; |
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 17 | import cuchaz.enigma.mapping.entry.FieldEntry; | 17 | import cuchaz.enigma.translation.representation.entry.FieldEntry; |
| 18 | import cuchaz.enigma.mapping.entry.MethodEntry; | 18 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 19 | 19 | ||
| 20 | public class TestEntryFactory { | 20 | public 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.analysis.ParsedJar; | 14 | import cuchaz.enigma.analysis.ParsedJar; |
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | 15 | import cuchaz.enigma.analysis.index.JarIndex; |
| 17 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 18 | import org.junit.Test; | 17 | import org.junit.Test; |
| 19 | 18 | ||
| 20 | import java.util.jar.JarFile; | 19 | import java.util.jar.JarFile; |
| 21 | 20 | ||
| 22 | import static cuchaz.enigma.TestEntryFactory.newClass; | 21 | import static cuchaz.enigma.TestEntryFactory.newClass; |
| 23 | import static org.hamcrest.MatcherAssert.assertThat; | 22 | import static org.hamcrest.MatcherAssert.assertThat; |
| 24 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 25 | import static org.hamcrest.Matchers.empty; | ||
| 26 | import static org.hamcrest.Matchers.is; | 23 | import static org.hamcrest.Matchers.is; |
| 27 | import static org.hamcrest.Matchers.nullValue; | ||
| 28 | 24 | ||
| 29 | public class TestInnerClasses { | 25 | public 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 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 15 | import cuchaz.enigma.analysis.JarIndex; | ||
| 16 | import cuchaz.enigma.analysis.ParsedJar; | 15 | import cuchaz.enigma.analysis.ParsedJar; |
| 17 | import cuchaz.enigma.mapping.entry.ClassEntry; | 16 | import cuchaz.enigma.analysis.index.JarIndex; |
| 18 | import cuchaz.enigma.mapping.entry.MethodDefEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.entry.MethodEntry; | 18 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 20 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | 19 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 21 | import org.junit.Test; | 20 | import org.junit.Test; |
| 22 | 21 | ||
| 23 | import java.io.File; | 22 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.*; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 15 | import cuchaz.enigma.mapping.entry.*; | 15 | import cuchaz.enigma.analysis.ParsedJar; |
| 16 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 17 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 18 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | ||
| 21 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 23 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 24 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 25 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | import org.junit.Test; | 26 | import org.junit.Test; |
| 27 | import org.objectweb.asm.Opcodes; | ||
| 17 | 28 | ||
| 18 | import java.util.Collection; | 29 | import java.util.Collection; |
| 19 | import java.util.Set; | ||
| 20 | import java.util.jar.JarFile; | 30 | import java.util.jar.JarFile; |
| 21 | 31 | ||
| 22 | import static cuchaz.enigma.TestEntryFactory.*; | 32 | import 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 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.analysis.*; | 14 | import cuchaz.enigma.analysis.*; |
| 15 | import cuchaz.enigma.mapping.*; | 15 | import cuchaz.enigma.analysis.index.EntryIndex; |
| 16 | import cuchaz.enigma.mapping.entry.*; | 16 | import cuchaz.enigma.analysis.index.InheritanceIndex; |
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 18 | import cuchaz.enigma.translation.VoidTranslator; | ||
| 19 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 23 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import org.junit.Test; | 24 | import org.junit.Test; |
| 18 | 25 | ||
| 19 | import java.util.Collection; | 26 | import java.util.Collection; |
| 20 | import java.util.Set; | 27 | import java.util.List; |
| 21 | import java.util.jar.JarFile; | 28 | import java.util.jar.JarFile; |
| 22 | 29 | ||
| 23 | import static cuchaz.enigma.TestEntryFactory.*; | 30 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.MethodDescriptor; | 14 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 15 | import cuchaz.enigma.mapping.TypeDescriptor; | 15 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 16 | import org.junit.Test; | 16 | import org.junit.Test; |
| 17 | 17 | ||
| 18 | import static org.hamcrest.MatcherAssert.assertThat; | 18 | import 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 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 17 | import org.junit.Test; | 17 | import org.junit.Test; |
| 18 | 18 | ||
| 19 | import java.io.File; | 19 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.MethodEntry; | 14 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 15 | import org.junit.Test; | 15 | import org.junit.Test; |
| 16 | 16 | ||
| 17 | import java.util.jar.JarFile; | 17 | import 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.entry.Entry; | 14 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 15 | import cuchaz.enigma.mapping.Mappings; | ||
| 16 | import cuchaz.enigma.mapping.Translator; | ||
| 17 | import org.junit.BeforeClass; | 15 | import org.junit.BeforeClass; |
| 18 | import org.junit.Test; | 16 | import org.junit.Test; |
| 19 | 17 | ||
| 20 | import static cuchaz.enigma.TestEntryFactory.newClass; | 18 | import static cuchaz.enigma.TestEntryFactory.*; |
| 21 | import static cuchaz.enigma.TestEntryFactory.newField; | ||
| 22 | import static cuchaz.enigma.TestEntryFactory.newMethod; | ||
| 23 | 19 | ||
| 24 | public class TestTranslator { | 20 | public 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 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import cuchaz.enigma.mapping.TypeDescriptor; | 14 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 15 | import org.junit.Test; | 15 | import org.junit.Test; |
| 16 | 16 | ||
| 17 | import static cuchaz.enigma.TestEntryFactory.newClass; | 17 | import 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; | |||
| 16 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 17 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.analysis.SourceIndex; |
| 18 | import cuchaz.enigma.analysis.Token; | 18 | import cuchaz.enigma.analysis.Token; |
| 19 | import cuchaz.enigma.mapping.entry.Entry; | 19 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 20 | 20 | ||
| 21 | import java.io.IOException; | 21 | import java.io.IOException; |
| 22 | import java.util.Collection; | 22 | import 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; |