diff options
| author | 2017-03-08 08:17:04 +0100 | |
|---|---|---|
| committer | 2017-03-08 08:17:04 +0100 | |
| commit | 6e464ea251cab63c776ece0b2a356f1498ffa294 (patch) | |
| tree | 5ed30c03f5ac4cd2d6877874f5ede576049954f7 | |
| parent | Drop unix case style and implement hashCode when equals is overrided (diff) | |
| download | enigma-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.gz enigma-6e464ea251cab63c776ece0b2a356f1498ffa294.tar.xz enigma-6e464ea251cab63c776ece0b2a356f1498ffa294.zip | |
Follow Fabric guidelines
159 files changed, 15475 insertions, 15653 deletions
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java index b0a4107c..f546eb18 100644 --- a/src/main/java/cuchaz/enigma/CommandMain.java +++ b/src/main/java/cuchaz/enigma/CommandMain.java | |||
| @@ -8,201 +8,198 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | 11 | ||
| 13 | import java.io.File; | 12 | package cuchaz.enigma; |
| 14 | import java.util.jar.JarFile; | ||
| 15 | 13 | ||
| 16 | import cuchaz.enigma.Deobfuscator.ProgressListener; | 14 | import cuchaz.enigma.Deobfuscator.ProgressListener; |
| 17 | import cuchaz.enigma.mapping.Mappings; | 15 | import cuchaz.enigma.mapping.Mappings; |
| 18 | import cuchaz.enigma.mapping.MappingsEnigmaReader; | 16 | import cuchaz.enigma.mapping.MappingsEnigmaReader; |
| 19 | 17 | ||
| 18 | import java.io.File; | ||
| 19 | import java.util.jar.JarFile; | ||
| 20 | |||
| 20 | public class CommandMain { | 21 | public class CommandMain { |
| 21 | 22 | ||
| 22 | public static class ConsoleProgressListener implements ProgressListener { | 23 | public static void main(String[] args) throws Exception { |
| 23 | 24 | try { | |
| 24 | private static final int ReportTime = 5000; // 5s | 25 | // process the command |
| 25 | 26 | String command = getArg(args, 0, "command", true); | |
| 26 | private int totalWork; | 27 | if (command.equalsIgnoreCase("deobfuscate")) { |
| 27 | private long startTime; | 28 | deobfuscate(args); |
| 28 | private long lastReportTime; | 29 | } else if (command.equalsIgnoreCase("decompile")) { |
| 29 | 30 | decompile(args); | |
| 30 | @Override | 31 | } else if (command.equalsIgnoreCase("protectify")) { |
| 31 | public void init(int totalWork, String title) { | 32 | protectify(args); |
| 32 | this.totalWork = totalWork; | 33 | } else if (command.equalsIgnoreCase("publify")) { |
| 33 | this.startTime = System.currentTimeMillis(); | 34 | publify(args); |
| 34 | this.lastReportTime = this.startTime; | 35 | } else if (command.equalsIgnoreCase("convertmappings")) { |
| 35 | System.out.println(title); | 36 | convertMappings(args); |
| 36 | } | 37 | } else { |
| 37 | 38 | throw new IllegalArgumentException("Command not recognized: " + command); | |
| 38 | @Override | 39 | } |
| 39 | public void onProgress(int numDone, String message) { | 40 | } catch (IllegalArgumentException ex) { |
| 40 | long now = System.currentTimeMillis(); | 41 | System.out.println(ex.getMessage()); |
| 41 | boolean isLastUpdate = numDone == this.totalWork; | 42 | printHelp(); |
| 42 | boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; | 43 | } |
| 43 | 44 | } | |
| 44 | if (shouldReport) { | 45 | |
| 45 | int percent = numDone * 100 / this.totalWork; | 46 | private static void printHelp() { |
| 46 | System.out.println(String.format("\tProgress: %3d%%", percent)); | 47 | System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); |
| 47 | this.lastReportTime = now; | 48 | System.out.println("Usage:"); |
| 48 | } | 49 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>"); |
| 49 | if (isLastUpdate) { | 50 | System.out.println("\twhere <command> is one of:"); |
| 50 | double elapsedSeconds = (now - this.startTime) / 1000.0; | 51 | System.out.println("\t\tdeobfuscate <in jar> <out jar> [<mappings file>]"); |
| 51 | System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); | 52 | System.out.println("\t\tdecompile <in jar> <out folder> [<mappings file>]"); |
| 52 | } | 53 | System.out.println("\t\tprotectify <in jar> <out jar>"); |
| 53 | } | 54 | System.out.println("\t\tpublify <in jar> <out jar>"); |
| 54 | } | 55 | System.out.println("\t\tconvertmappings <enigma mappings> <converted mappings> <ENIGMA_FILE|ENIGMA_DIRECTORY|SRG_FILE>"); |
| 55 | 56 | } | |
| 56 | public static void main(String[] args) throws Exception { | 57 | |
| 57 | try { | 58 | private static void decompile(String[] args) throws Exception { |
| 58 | // process the command | 59 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 59 | String command = getArg(args, 0, "command", true); | 60 | File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); |
| 60 | if (command.equalsIgnoreCase("deobfuscate")) { | 61 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); |
| 61 | deobfuscate(args); | 62 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 62 | } else if (command.equalsIgnoreCase("decompile")) { | 63 | deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); |
| 63 | decompile(args); | 64 | } |
| 64 | } else if (command.equalsIgnoreCase("protectify")) { | 65 | |
| 65 | protectify(args); | 66 | private static void deobfuscate(String[] args) throws Exception { |
| 66 | } else if (command.equalsIgnoreCase("publify")) { | 67 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 67 | publify(args); | 68 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); |
| 68 | } else if (command.equalsIgnoreCase("convertmappings")) { | 69 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); |
| 69 | convertMappings(args); | 70 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 70 | } | 71 | deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); |
| 71 | else { | 72 | } |
| 72 | throw new IllegalArgumentException("Command not recognized: " + command); | 73 | |
| 73 | } | 74 | private static void protectify(String[] args) throws Exception { |
| 74 | } catch (IllegalArgumentException ex) { | 75 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 75 | System.out.println(ex.getMessage()); | 76 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); |
| 76 | printHelp(); | 77 | Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); |
| 77 | } | 78 | deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); |
| 78 | } | 79 | } |
| 79 | 80 | ||
| 80 | private static void printHelp() { | 81 | private static void publify(String[] args) throws Exception { |
| 81 | System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); | 82 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 82 | System.out.println("Usage:"); | 83 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); |
| 83 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>"); | 84 | Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); |
| 84 | System.out.println("\twhere <command> is one of:"); | 85 | deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener()); |
| 85 | System.out.println("\t\tdeobfuscate <in jar> <out jar> [<mappings file>]"); | 86 | } |
| 86 | System.out.println("\t\tdecompile <in jar> <out folder> [<mappings file>]"); | 87 | |
| 87 | System.out.println("\t\tprotectify <in jar> <out jar>"); | 88 | private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { |
| 88 | System.out.println("\t\tpublify <in jar> <out jar>"); | 89 | System.out.println("Reading jar..."); |
| 89 | System.out.println("\t\tconvertmappings <enigma mappings> <converted mappings> <ENIGMA_FILE|ENIGMA_DIRECTORY|SRG_FILE>"); | 90 | Deobfuscator deobfuscator = new Deobfuscator(jar); |
| 90 | } | 91 | if (fileMappings != null) { |
| 91 | 92 | System.out.println("Reading mappings..."); | |
| 92 | private static void decompile(String[] args) throws Exception { | 93 | Mappings mappings = new MappingsEnigmaReader().read(fileMappings); |
| 93 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); | 94 | deobfuscator.setMappings(mappings); |
| 94 | File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); | 95 | } |
| 95 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); | 96 | return deobfuscator; |
| 96 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 97 | } |
| 97 | deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); | 98 | |
| 98 | } | 99 | private static void convertMappings(String[] args) throws Exception { |
| 99 | 100 | File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true)); | |
| 100 | private static void deobfuscate(String[] args) throws Exception { | 101 | File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); |
| 101 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); | 102 | String name = getArg(args, 3, "format type", true); |
| 102 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); | 103 | Mappings.FormatType formatType; |
| 103 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); | 104 | try { |
| 104 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 105 | formatType = Mappings.FormatType.valueOf(name.toUpperCase()); |
| 105 | deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); | 106 | } catch (IllegalArgumentException e) { |
| 106 | } | 107 | throw new IllegalArgumentException(name + "is not a valid mapping format!"); |
| 107 | 108 | } | |
| 108 | private static void protectify(String[] args) throws Exception { | 109 | |
| 109 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); | 110 | System.out.println("Reading mappings..."); |
| 110 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); | 111 | Mappings mappings = new MappingsEnigmaReader().read(fileMappings); |
| 111 | Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); | 112 | System.out.println("Saving new mappings..."); |
| 112 | deobfuscator.protectifyJar(fileJarOut, new ConsoleProgressListener()); | 113 | switch (formatType) { |
| 113 | } | 114 | case SRG_FILE: |
| 114 | 115 | mappings.saveSRGMappings(result); | |
| 115 | private static void publify(String[] args) throws Exception { | 116 | break; |
| 116 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); | 117 | default: |
| 117 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); | 118 | mappings.saveEnigmaMappings(result, Mappings.FormatType.ENIGMA_FILE != formatType); |
| 118 | Deobfuscator deobfuscator = getDeobfuscator(null, new JarFile(fileJarIn)); | 119 | break; |
| 119 | deobfuscator.publifyJar(fileJarOut, new ConsoleProgressListener()); | 120 | } |
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { | 123 | private static String getArg(String[] args, int i, String name, boolean required) { |
| 123 | System.out.println("Reading jar..."); | 124 | if (i >= args.length) { |
| 124 | Deobfuscator deobfuscator = new Deobfuscator(jar); | 125 | if (required) { |
| 125 | if (fileMappings != null) { | 126 | throw new IllegalArgumentException(name + " is required"); |
| 126 | System.out.println("Reading mappings..."); | 127 | } else { |
| 127 | Mappings mappings = new MappingsEnigmaReader().read(fileMappings); | 128 | return null; |
| 128 | deobfuscator.setMappings(mappings); | 129 | } |
| 129 | } | 130 | } |
| 130 | return deobfuscator; | 131 | return args[i]; |
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | private static void convertMappings(String[] args) throws Exception { | 134 | private static File getWritableFile(String path) { |
| 134 | File fileMappings = getReadableFile(getArg(args, 1, "enigma mapping", true)); | 135 | if (path == null) { |
| 135 | File result = getWritableFile(getArg(args, 2, "enigma mapping", true)); | 136 | return null; |
| 136 | String name = getArg(args, 3, "format type", true); | 137 | } |
| 137 | Mappings.FormatType formatType; | 138 | File file = new File(path).getAbsoluteFile(); |
| 138 | try | 139 | File dir = file.getParentFile(); |
| 139 | { | 140 | if (dir == null) { |
| 140 | formatType = Mappings.FormatType.valueOf(name.toUpperCase()); | 141 | throw new IllegalArgumentException("Cannot write file: " + path); |
| 141 | } catch (IllegalArgumentException e) | 142 | } |
| 142 | { | 143 | // quick fix to avoid stupid stuff in Gradle code |
| 143 | throw new IllegalArgumentException(name + "is not a valid mapping format!"); | 144 | if (!dir.isDirectory()) { |
| 144 | } | 145 | dir.mkdirs(); |
| 145 | 146 | } | |
| 146 | System.out.println("Reading mappings..."); | 147 | return file; |
| 147 | Mappings mappings = new MappingsEnigmaReader().read(fileMappings); | 148 | } |
| 148 | System.out.println("Saving new mappings..."); | 149 | |
| 149 | switch (formatType) | 150 | private static File getWritableFolder(String path) { |
| 150 | { | 151 | if (path == null) { |
| 151 | case SRG_FILE: | 152 | return null; |
| 152 | mappings.saveSRGMappings(result); | 153 | } |
| 153 | break; | 154 | File dir = new File(path).getAbsoluteFile(); |
| 154 | default: | 155 | if (!dir.exists()) { |
| 155 | mappings.saveEnigmaMappings(result, Mappings.FormatType.ENIGMA_FILE != formatType); | 156 | throw new IllegalArgumentException("Cannot write to folder: " + dir); |
| 156 | break; | 157 | } |
| 157 | } | 158 | return dir; |
| 158 | } | 159 | } |
| 159 | 160 | ||
| 160 | private static String getArg(String[] args, int i, String name, boolean required) { | 161 | private static File getReadableFile(String path) { |
| 161 | if (i >= args.length) { | 162 | if (path == null) { |
| 162 | if (required) { | 163 | return null; |
| 163 | throw new IllegalArgumentException(name + " is required"); | 164 | } |
| 164 | } else { | 165 | File file = new File(path).getAbsoluteFile(); |
| 165 | return null; | 166 | if (!file.exists()) { |
| 166 | } | 167 | throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); |
| 167 | } | 168 | } |
| 168 | return args[i]; | 169 | return file; |
| 169 | } | 170 | } |
| 170 | 171 | ||
| 171 | private static File getWritableFile(String path) { | 172 | public static class ConsoleProgressListener implements ProgressListener { |
| 172 | if (path == null) { | 173 | |
| 173 | return null; | 174 | private static final int ReportTime = 5000; // 5s |
| 174 | } | 175 | |
| 175 | File file = new File(path).getAbsoluteFile(); | 176 | private int totalWork; |
| 176 | File dir = file.getParentFile(); | 177 | private long startTime; |
| 177 | if (dir == null) { | 178 | private long lastReportTime; |
| 178 | throw new IllegalArgumentException("Cannot write file: " + path); | 179 | |
| 179 | } | 180 | @Override |
| 180 | // quick fix to avoid stupid stuff in Gradle code | 181 | public void init(int totalWork, String title) { |
| 181 | if (!dir.isDirectory()) { | 182 | this.totalWork = totalWork; |
| 182 | dir.mkdirs(); | 183 | this.startTime = System.currentTimeMillis(); |
| 183 | } | 184 | this.lastReportTime = this.startTime; |
| 184 | return file; | 185 | System.out.println(title); |
| 185 | } | 186 | } |
| 186 | 187 | ||
| 187 | private static File getWritableFolder(String path) { | 188 | @Override |
| 188 | if (path == null) { | 189 | public void onProgress(int numDone, String message) { |
| 189 | return null; | 190 | long now = System.currentTimeMillis(); |
| 190 | } | 191 | boolean isLastUpdate = numDone == this.totalWork; |
| 191 | File dir = new File(path).getAbsoluteFile(); | 192 | boolean shouldReport = isLastUpdate || now - this.lastReportTime > ReportTime; |
| 192 | if (!dir.exists()) { | 193 | |
| 193 | throw new IllegalArgumentException("Cannot write to folder: " + dir); | 194 | if (shouldReport) { |
| 194 | } | 195 | int percent = numDone * 100 / this.totalWork; |
| 195 | return dir; | 196 | System.out.println(String.format("\tProgress: %3d%%", percent)); |
| 196 | } | 197 | this.lastReportTime = now; |
| 197 | 198 | } | |
| 198 | private static File getReadableFile(String path) { | 199 | if (isLastUpdate) { |
| 199 | if (path == null) { | 200 | double elapsedSeconds = (now - this.startTime) / 1000.0; |
| 200 | return null; | 201 | System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); |
| 201 | } | 202 | } |
| 202 | File file = new File(path).getAbsoluteFile(); | 203 | } |
| 203 | if (!file.exists()) { | 204 | } |
| 204 | throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); | ||
| 205 | } | ||
| 206 | return file; | ||
| 207 | } | ||
| 208 | } | 205 | } |
diff --git a/src/main/java/cuchaz/enigma/Constants.java b/src/main/java/cuchaz/enigma/Constants.java index 04730480..b08438a3 100644 --- a/src/main/java/cuchaz/enigma/Constants.java +++ b/src/main/java/cuchaz/enigma/Constants.java | |||
| @@ -8,12 +8,13 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | public class Constants { | 14 | public class Constants { |
| 14 | public static final String NAME = "Enigma"; | 15 | public static final String NAME = "Enigma"; |
| 15 | public static final String VERSION = "0.11.0 (Fabric Fork)"; | 16 | public static final String VERSION = "0.11.0 (Fabric Fork)"; |
| 16 | public static final String URL = "http://www.cuchazinteractive.com/enigma"; | 17 | public static final String URL = "http://www.cuchazinteractive.com/enigma"; |
| 17 | public static final int MiB = 1024 * 1024; // 1 mebibyte | 18 | public static final int MiB = 1024 * 1024; // 1 mebibyte |
| 18 | public static final int KiB = 1024; // 1 kebibyte | 19 | public static final int KiB = 1024; // 1 kebibyte |
| 19 | } | 20 | } |
diff --git a/src/main/java/cuchaz/enigma/ConvertMain.java b/src/main/java/cuchaz/enigma/ConvertMain.java index 48e7f27c..3d58f57c 100644 --- a/src/main/java/cuchaz/enigma/ConvertMain.java +++ b/src/main/java/cuchaz/enigma/ConvertMain.java | |||
| @@ -8,11 +8,8 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | 11 | ||
| 13 | import java.io.File; | 12 | package cuchaz.enigma; |
| 14 | import java.io.IOException; | ||
| 15 | import java.util.jar.JarFile; | ||
| 16 | 13 | ||
| 17 | import cuchaz.enigma.convert.*; | 14 | import cuchaz.enigma.convert.*; |
| 18 | import cuchaz.enigma.gui.ClassMatchingGui; | 15 | import cuchaz.enigma.gui.ClassMatchingGui; |
| @@ -21,338 +18,340 @@ import cuchaz.enigma.mapping.*; | |||
| 21 | import cuchaz.enigma.throwables.MappingConflict; | 18 | import cuchaz.enigma.throwables.MappingConflict; |
| 22 | import cuchaz.enigma.throwables.MappingParseException; | 19 | import cuchaz.enigma.throwables.MappingParseException; |
| 23 | 20 | ||
| 21 | import java.io.File; | ||
| 22 | import java.io.IOException; | ||
| 23 | import java.util.jar.JarFile; | ||
| 24 | 24 | ||
| 25 | public class ConvertMain { | 25 | public class ConvertMain { |
| 26 | 26 | ||
| 27 | public static void main(String[] args) | 27 | public static void main(String[] args) |
| 28 | throws IOException, MappingParseException { | 28 | throws IOException, MappingParseException { |
| 29 | try { | 29 | try { |
| 30 | //Get all are args | 30 | //Get all are args |
| 31 | String JarOld = getArg(args, 1, "Path to Old Jar", true); | 31 | String JarOld = getArg(args, 1, "Path to Old Jar", true); |
| 32 | String JarNew = getArg(args, 2, "Path to New Jar", true); | 32 | String JarNew = getArg(args, 2, "Path to New Jar", true); |
| 33 | String OldMappings = getArg(args, 3, "Path to old .mappings file", true); | 33 | String OldMappings = getArg(args, 3, "Path to old .mappings file", true); |
| 34 | String NewMappings = getArg(args, 4, "Path to new .mappings file", true); | 34 | String NewMappings = getArg(args, 4, "Path to new .mappings file", true); |
| 35 | String ClassMatches = getArg(args, 5, "Path to Class .matches file", true); | 35 | String ClassMatches = getArg(args, 5, "Path to Class .matches file", true); |
| 36 | String FieldMatches = getArg(args, 6, "Path to Field .matches file", true); | 36 | String FieldMatches = getArg(args, 6, "Path to Field .matches file", true); |
| 37 | String MethodMatches = getArg(args, 7, "Path to Method .matches file", true); | 37 | String MethodMatches = getArg(args, 7, "Path to Method .matches file", true); |
| 38 | //OldJar | 38 | //OldJar |
| 39 | JarFile sourceJar = new JarFile(new File(JarOld)); | 39 | JarFile sourceJar = new JarFile(new File(JarOld)); |
| 40 | //NewJar | 40 | //NewJar |
| 41 | JarFile destJar = new JarFile(new File(JarNew)); | 41 | JarFile destJar = new JarFile(new File(JarNew)); |
| 42 | //Get the mapping files | 42 | //Get the mapping files |
| 43 | File inMappingsFile = new File(OldMappings); | 43 | File inMappingsFile = new File(OldMappings); |
| 44 | File outMappingsFile = new File(NewMappings); | 44 | File outMappingsFile = new File(NewMappings); |
| 45 | Mappings mappings = new MappingsEnigmaReader().read(inMappingsFile); | 45 | Mappings mappings = new MappingsEnigmaReader().read(inMappingsFile); |
| 46 | //Make the Match Files.. | 46 | //Make the Match Files.. |
| 47 | File classMatchesFile = new File(ClassMatches); | 47 | File classMatchesFile = new File(ClassMatches); |
| 48 | File fieldMatchesFile = new File(FieldMatches); | 48 | File fieldMatchesFile = new File(FieldMatches); |
| 49 | File methodMatchesFile = new File(MethodMatches); | 49 | File methodMatchesFile = new File(MethodMatches); |
| 50 | 50 | ||
| 51 | String command = getArg(args, 0, "command", true); | 51 | String command = getArg(args, 0, "command", true); |
| 52 | 52 | ||
| 53 | if (command.equalsIgnoreCase("computeClassMatches")) { | 53 | if (command.equalsIgnoreCase("computeClassMatches")) { |
| 54 | computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); | 54 | computeClassMatches(classMatchesFile, sourceJar, destJar, mappings); |
| 55 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); | 55 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); |
| 56 | } else if (command.equalsIgnoreCase("editClassMatches")) { | 56 | } else if (command.equalsIgnoreCase("editClassMatches")) { |
| 57 | editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); | 57 | editClasssMatches(classMatchesFile, sourceJar, destJar, mappings); |
| 58 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); | 58 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile); |
| 59 | } else if (command.equalsIgnoreCase("computeFieldMatches")) { | 59 | } else if (command.equalsIgnoreCase("computeFieldMatches")) { |
| 60 | computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); | 60 | computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); |
| 61 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); | 61 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); |
| 62 | } else if (command.equalsIgnoreCase("editFieldMatches")) { | 62 | } else if (command.equalsIgnoreCase("editFieldMatches")) { |
| 63 | editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); | 63 | editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); |
| 64 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); | 64 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile); |
| 65 | } else if (command.equalsIgnoreCase("computeMethodMatches")) { | 65 | } else if (command.equalsIgnoreCase("computeMethodMatches")) { |
| 66 | computeMethodMatches(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); | 66 | computeMethodMatches(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); |
| 67 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); | 67 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); |
| 68 | } else if (command.equalsIgnoreCase("editMethodMatches")) { | 68 | } else if (command.equalsIgnoreCase("editMethodMatches")) { |
| 69 | editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); | 69 | editMethodMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, methodMatchesFile); |
| 70 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); | 70 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); |
| 71 | } else if (command.equalsIgnoreCase("convertMappings")) { | 71 | } else if (command.equalsIgnoreCase("convertMappings")) { |
| 72 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); | 72 | convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchesFile, fieldMatchesFile, methodMatchesFile); |
| 73 | } | 73 | } |
| 74 | } catch (MappingConflict ex) { | 74 | } catch (MappingConflict ex) { |
| 75 | System.out.println(ex.getMessage()); | 75 | System.out.println(ex.getMessage()); |
| 76 | ex.printStackTrace(); | 76 | ex.printStackTrace(); |
| 77 | } catch (IllegalArgumentException ex) { | 77 | } catch (IllegalArgumentException ex) { |
| 78 | System.out.println(ex.getMessage()); | 78 | System.out.println(ex.getMessage()); |
| 79 | printHelp(); | 79 | printHelp(); |
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | private static void printHelp() { | 83 | private static void printHelp() { |
| 84 | System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); | 84 | System.out.println(String.format("%s - %s", Constants.NAME, Constants.VERSION)); |
| 85 | System.out.println("Usage:"); | 85 | System.out.println("Usage:"); |
| 86 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain <command> <old-jar> <new-jar> <old-mappings> <new-mappings> <class-matches> <field-matches> <method-matches>"); | 86 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.ConvertMain <command> <old-jar> <new-jar> <old-mappings> <new-mappings> <class-matches> <field-matches> <method-matches>"); |
| 87 | System.out.println("\tWhere <command> is one of:"); | 87 | System.out.println("\tWhere <command> is one of:"); |
| 88 | System.out.println("\t\tcomputeClassMatches"); | 88 | System.out.println("\t\tcomputeClassMatches"); |
| 89 | System.out.println("\t\teditClassMatches"); | 89 | System.out.println("\t\teditClassMatches"); |
| 90 | System.out.println("\t\tcomputeFieldMatches"); | 90 | System.out.println("\t\tcomputeFieldMatches"); |
| 91 | System.out.println("\t\teditFieldMatches"); | 91 | System.out.println("\t\teditFieldMatches"); |
| 92 | System.out.println("\t\teditMethodMatches"); | 92 | System.out.println("\t\teditMethodMatches"); |
| 93 | System.out.println("\t\tconvertMappings"); | 93 | System.out.println("\t\tconvertMappings"); |
| 94 | System.out.println("\tWhere <old-jar> is the already mapped jar."); | 94 | System.out.println("\tWhere <old-jar> is the already mapped jar."); |
| 95 | System.out.println("\tWhere <new-jar> is the unmapped jar."); | 95 | System.out.println("\tWhere <new-jar> is the unmapped jar."); |
| 96 | System.out.println("\tWhere <old-mappings> is the path to the mappings for the old jar."); | 96 | System.out.println("\tWhere <old-mappings> is the path to the mappings for the old jar."); |
| 97 | System.out.println("\tWhere <new-mappings> is the new mappings. (Where you want to save them and there name)"); | 97 | System.out.println("\tWhere <new-mappings> is the new mappings. (Where you want to save them and there name)"); |
| 98 | System.out.println("\tWhere <class-matches> is the class matches file."); | 98 | System.out.println("\tWhere <class-matches> is the class matches file."); |
| 99 | System.out.println("\tWhere <field-matches> is the field matches file."); | 99 | System.out.println("\tWhere <field-matches> is the field matches file."); |
| 100 | System.out.println("\tWhere <method-matches> is the method matches file."); | 100 | System.out.println("\tWhere <method-matches> is the method matches file."); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | //Copy of getArg from CommandMain.... Should make a utils class. | 103 | //Copy of getArg from CommandMain.... Should make a utils class. |
| 104 | private static String getArg(String[] args, int i, String name, boolean required) { | 104 | private static String getArg(String[] args, int i, String name, boolean required) { |
| 105 | if (i >= args.length) { | 105 | if (i >= args.length) { |
| 106 | if (required) { | 106 | if (required) { |
| 107 | throw new IllegalArgumentException(name + " is required"); | 107 | throw new IllegalArgumentException(name + " is required"); |
| 108 | } else { | 108 | } else { |
| 109 | return null; | 109 | return null; |
| 110 | } | 110 | } |
| 111 | } | 111 | } |
| 112 | return args[i]; | 112 | return args[i]; |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) | 115 | private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) |
| 116 | throws IOException { | 116 | throws IOException { |
| 117 | ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings); | 117 | ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings); |
| 118 | MatchesWriter.writeClasses(classMatches, classMatchesFile); | 118 | MatchesWriter.writeClasses(classMatches, classMatchesFile); |
| 119 | System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); | 119 | System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) | 122 | private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) |
| 123 | throws IOException { | 123 | throws IOException { |
| 124 | System.out.println("Reading class matches..."); | 124 | System.out.println("Reading class matches..."); |
| 125 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 125 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 126 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 126 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); |
| 127 | deobfuscators.source.setMappings(mappings); | 127 | deobfuscators.source.setMappings(mappings); |
| 128 | System.out.println("Starting GUI..."); | 128 | System.out.println("Starting GUI..."); |
| 129 | new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> | 129 | new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(matches -> |
| 130 | { | 130 | { |
| 131 | try { | 131 | try { |
| 132 | MatchesWriter.writeClasses(matches, classMatchesFile); | 132 | MatchesWriter.writeClasses(matches, classMatchesFile); |
| 133 | } catch (IOException ex) { | 133 | } catch (IOException ex) { |
| 134 | throw new Error(ex); | 134 | throw new Error(ex); |
| 135 | } | 135 | } |
| 136 | }); | 136 | }); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | @SuppressWarnings("unused") | 139 | @SuppressWarnings("unused") |
| 140 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) | 140 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) |
| 141 | throws IOException, MappingConflict { | 141 | throws IOException, MappingConflict { |
| 142 | System.out.println("Reading class matches..."); | 142 | System.out.println("Reading class matches..."); |
| 143 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 143 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 144 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 144 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); |
| 145 | deobfuscators.source.setMappings(mappings); | 145 | deobfuscators.source.setMappings(mappings); |
| 146 | 146 | ||
| 147 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); | 147 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); |
| 148 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); | 148 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); |
| 149 | System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); | 149 | System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) | 152 | private static void computeFieldMatches(File memberMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) |
| 153 | throws IOException, MappingParseException { | 153 | throws IOException, MappingParseException { |
| 154 | 154 | ||
| 155 | System.out.println("Reading class matches..."); | 155 | System.out.println("Reading class matches..."); |
| 156 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 156 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 157 | System.out.println("Reading mappings..."); | 157 | System.out.println("Reading mappings..."); |
| 158 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); | 158 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); |
| 159 | System.out.println("Indexing dest jar..."); | 159 | System.out.println("Indexing dest jar..."); |
| 160 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); | 160 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); |
| 161 | 161 | ||
| 162 | System.out.println("Writing matches..."); | 162 | System.out.println("Writing matches..."); |
| 163 | 163 | ||
| 164 | // get the matched and unmatched mappings | 164 | // get the matched and unmatched mappings |
| 165 | MemberMatches<FieldEntry> fieldMatches = MappingsConverter.computeMemberMatches( | 165 | MemberMatches<FieldEntry> fieldMatches = MappingsConverter.computeMemberMatches( |
| 166 | destDeobfuscator, | 166 | destDeobfuscator, |
| 167 | destMappings, | 167 | destMappings, |
| 168 | classMatches, | 168 | classMatches, |
| 169 | MappingsConverter.getFieldDoer() | 169 | MappingsConverter.getFieldDoer() |
| 170 | ); | 170 | ); |
| 171 | 171 | ||
| 172 | MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); | 172 | MatchesWriter.writeMembers(fieldMatches, memberMatchesFile); |
| 173 | System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); | 173 | System.out.println("Wrote:\n\t" + memberMatchesFile.getAbsolutePath()); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) | 176 | private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) |
| 177 | throws IOException, MappingParseException { | 177 | throws IOException, MappingParseException { |
| 178 | 178 | ||
| 179 | System.out.println("Reading matches..."); | 179 | System.out.println("Reading matches..."); |
| 180 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 180 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 181 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); | 181 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); |
| 182 | 182 | ||
| 183 | // prep deobfuscators | 183 | // prep deobfuscators |
| 184 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 184 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); |
| 185 | deobfuscators.source.setMappings(sourceMappings); | 185 | deobfuscators.source.setMappings(sourceMappings); |
| 186 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); | 186 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); |
| 187 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); | 187 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); |
| 188 | checker.dropBrokenMappings(destMappings); | 188 | checker.dropBrokenMappings(destMappings); |
| 189 | deobfuscators.dest.setMappings(destMappings); | 189 | deobfuscators.dest.setMappings(destMappings); |
| 190 | 190 | ||
| 191 | new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( | 191 | new MemberMatchingGui<>(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( |
| 192 | matches -> | 192 | matches -> |
| 193 | { | 193 | { |
| 194 | try { | 194 | try { |
| 195 | MatchesWriter.writeMembers(matches, fieldMatchesFile); | 195 | MatchesWriter.writeMembers(matches, fieldMatchesFile); |
| 196 | } catch (IOException ex) { | 196 | } catch (IOException ex) { |
| 197 | throw new Error(ex); | 197 | throw new Error(ex); |
| 198 | } | 198 | } |
| 199 | }); | 199 | }); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | @SuppressWarnings("unused") | 202 | @SuppressWarnings("unused") |
| 203 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile) | 203 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile) |
| 204 | throws IOException, MappingConflict { | 204 | throws IOException, MappingConflict { |
| 205 | 205 | ||
| 206 | System.out.println("Reading matches..."); | 206 | System.out.println("Reading matches..."); |
| 207 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 207 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 208 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); | 208 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); |
| 209 | 209 | ||
| 210 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 210 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); |
| 211 | deobfuscators.source.setMappings(mappings); | 211 | deobfuscators.source.setMappings(mappings); |
| 212 | 212 | ||
| 213 | // apply matches | 213 | // apply matches |
| 214 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); | 214 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); |
| 215 | MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); | 215 | MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); |
| 216 | 216 | ||
| 217 | // write out the converted mappings | 217 | // write out the converted mappings |
| 218 | 218 | ||
| 219 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); | 219 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); |
| 220 | System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); | 220 | System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | 223 | private static void computeMethodMatches(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings sourceMappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) | |
| 224 | private static void computeMethodMatches(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings sourceMappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) | 224 | throws IOException, MappingParseException { |
| 225 | throws IOException, MappingParseException { | 225 | |
| 226 | 226 | System.out.println("Reading class matches..."); | |
| 227 | System.out.println("Reading class matches..."); | 227 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 228 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 228 | System.out.println("Reading dest mappings..."); |
| 229 | System.out.println("Reading dest mappings..."); | 229 | Mappings destMappings = new MappingsEnigmaReader().read(outMappingsFile); |
| 230 | Mappings destMappings = new MappingsEnigmaReader().read(outMappingsFile); | 230 | System.out.println("Indexing dest jar..."); |
| 231 | System.out.println("Indexing dest jar..."); | 231 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); |
| 232 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); | 232 | System.out.println("Indexing source jar..."); |
| 233 | System.out.println("Indexing source jar..."); | 233 | Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); |
| 234 | Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); | 234 | |
| 235 | 235 | System.out.println("Writing method matches..."); | |
| 236 | System.out.println("Writing method matches..."); | 236 | |
| 237 | 237 | // get the matched and unmatched mappings | |
| 238 | // get the matched and unmatched mappings | 238 | MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMethodsMatches( |
| 239 | MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMethodsMatches( | 239 | destDeobfuscator, |
| 240 | destDeobfuscator, | 240 | destMappings, |
| 241 | destMappings, | 241 | sourceDeobfuscator, |
| 242 | sourceDeobfuscator, | 242 | sourceMappings, |
| 243 | sourceMappings, | 243 | classMatches, |
| 244 | classMatches, | 244 | MappingsConverter.getMethodDoer() |
| 245 | MappingsConverter.getMethodDoer() | 245 | ); |
| 246 | ); | 246 | |
| 247 | 247 | MatchesWriter.writeMembers(methodMatches, methodMatchesFile); | |
| 248 | MatchesWriter.writeMembers(methodMatches, methodMatchesFile); | 248 | System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); |
| 249 | System.out.println("Wrote:\n\t" + methodMatchesFile.getAbsolutePath()); | 249 | } |
| 250 | } | 250 | |
| 251 | 251 | private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile) | |
| 252 | private static void editMethodMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File methodMatchesFile) | 252 | throws IOException, MappingParseException { |
| 253 | throws IOException, MappingParseException { | 253 | |
| 254 | 254 | System.out.println("Reading matches..."); | |
| 255 | System.out.println("Reading matches..."); | 255 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 256 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 256 | MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); |
| 257 | MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); | 257 | |
| 258 | 258 | // prep deobfuscators | |
| 259 | // prep deobfuscators | 259 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); |
| 260 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 260 | deobfuscators.source.setMappings(sourceMappings); |
| 261 | deobfuscators.source.setMappings(sourceMappings); | 261 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); |
| 262 | Mappings destMappings = new MappingsEnigmaReader().read(destMappingsFile); | 262 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); |
| 263 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); | 263 | checker.dropBrokenMappings(destMappings); |
| 264 | checker.dropBrokenMappings(destMappings); | 264 | deobfuscators.dest.setMappings(destMappings); |
| 265 | deobfuscators.dest.setMappings(destMappings); | 265 | |
| 266 | 266 | new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( | |
| 267 | new MemberMatchingGui<>(classMatches, methodMatches, deobfuscators.source, deobfuscators.dest).setSaveListener( | 267 | matches -> |
| 268 | matches -> | 268 | { |
| 269 | { | 269 | try { |
| 270 | try { | 270 | MatchesWriter.writeMembers(matches, methodMatchesFile); |
| 271 | MatchesWriter.writeMembers(matches, methodMatchesFile); | 271 | } catch (IOException ex) { |
| 272 | } catch (IOException ex) { | 272 | throw new Error(ex); |
| 273 | throw new Error(ex); | 273 | } |
| 274 | } | 274 | }); |
| 275 | }); | 275 | } |
| 276 | } | 276 | |
| 277 | 277 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) | |
| 278 | private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile, File fieldMatchesFile, File methodMatchesFile) | 278 | throws IOException, MappingConflict { |
| 279 | throws IOException, MappingConflict { | 279 | |
| 280 | 280 | System.out.println("Reading matches..."); | |
| 281 | System.out.println("Reading matches..."); | 281 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); |
| 282 | ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); | 282 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); |
| 283 | MemberMatches<FieldEntry> fieldMatches = MatchesReader.readMembers(fieldMatchesFile); | 283 | MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); |
| 284 | MemberMatches<BehaviorEntry> methodMatches = MatchesReader.readMembers(methodMatchesFile); | 284 | |
| 285 | 285 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | |
| 286 | Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); | 286 | deobfuscators.source.setMappings(mappings); |
| 287 | deobfuscators.source.setMappings(mappings); | 287 | |
| 288 | 288 | // apply matches | |
| 289 | // apply matches | 289 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); |
| 290 | Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.dest); | 290 | MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); |
| 291 | MappingsConverter.applyMemberMatches(newMappings, classMatches, fieldMatches, MappingsConverter.getFieldDoer()); | 291 | MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer()); |
| 292 | MappingsConverter.applyMemberMatches(newMappings, classMatches, methodMatches, MappingsConverter.getMethodDoer()); | 292 | |
| 293 | 293 | // check the final mappings | |
| 294 | // check the final mappings | 294 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); |
| 295 | MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); | 295 | checker.dropBrokenMappings(newMappings); |
| 296 | checker.dropBrokenMappings(newMappings); | 296 | |
| 297 | 297 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { | |
| 298 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { | 298 | System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); |
| 299 | System.out.println("WARNING: Broken class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); | 299 | } |
| 300 | } | 300 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { |
| 301 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { | 301 | System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); |
| 302 | System.out.println("WARNING: Broken inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); | 302 | } |
| 303 | } | 303 | for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { |
| 304 | for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { | 304 | System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); |
| 305 | System.out.println("WARNING: Broken field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); | 305 | } |
| 306 | } | 306 | for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { |
| 307 | for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { | 307 | System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); |
| 308 | System.out.println("WARNING: Broken behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ")"); | 308 | } |
| 309 | } | 309 | |
| 310 | 310 | // write out the converted mappings | |
| 311 | // write out the converted mappings | 311 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); |
| 312 | new MappingsEnigmaWriter().write(outMappingsFile, newMappings, true); | 312 | System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); |
| 313 | System.out.println("Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath()); | 313 | } |
| 314 | } | 314 | |
| 315 | 315 | private static class Deobfuscators { | |
| 316 | private static class Deobfuscators { | 316 | |
| 317 | 317 | public Deobfuscator source; | |
| 318 | public Deobfuscator source; | 318 | public Deobfuscator dest; |
| 319 | public Deobfuscator dest; | 319 | |
| 320 | 320 | public Deobfuscators(JarFile sourceJar, JarFile destJar) { | |
| 321 | public Deobfuscators(JarFile sourceJar, JarFile destJar) { | 321 | System.out.println("Indexing source jar..."); |
| 322 | System.out.println("Indexing source jar..."); | 322 | IndexerThread sourceIndexer = new IndexerThread(sourceJar); |
| 323 | IndexerThread sourceIndexer = new IndexerThread(sourceJar); | 323 | sourceIndexer.start(); |
| 324 | sourceIndexer.start(); | 324 | System.out.println("Indexing dest jar..."); |
| 325 | System.out.println("Indexing dest jar..."); | 325 | IndexerThread destIndexer = new IndexerThread(destJar); |
| 326 | IndexerThread destIndexer = new IndexerThread(destJar); | 326 | destIndexer.start(); |
| 327 | destIndexer.start(); | 327 | sourceIndexer.joinOrBail(); |
| 328 | sourceIndexer.joinOrBail(); | 328 | destIndexer.joinOrBail(); |
| 329 | destIndexer.joinOrBail(); | 329 | source = sourceIndexer.deobfuscator; |
| 330 | source = sourceIndexer.deobfuscator; | 330 | dest = destIndexer.deobfuscator; |
| 331 | dest = destIndexer.deobfuscator; | 331 | } |
| 332 | } | 332 | } |
| 333 | } | 333 | |
| 334 | 334 | private static class IndexerThread extends Thread { | |
| 335 | private static class IndexerThread extends Thread { | 335 | |
| 336 | 336 | public Deobfuscator deobfuscator; | |
| 337 | private JarFile jarFile; | 337 | private JarFile jarFile; |
| 338 | public Deobfuscator deobfuscator; | 338 | |
| 339 | 339 | public IndexerThread(JarFile jarFile) { | |
| 340 | public IndexerThread(JarFile jarFile) { | 340 | this.jarFile = jarFile; |
| 341 | this.jarFile = jarFile; | 341 | deobfuscator = null; |
| 342 | deobfuscator = null; | 342 | } |
| 343 | } | 343 | |
| 344 | 344 | public void joinOrBail() { | |
| 345 | public void joinOrBail() { | 345 | try { |
| 346 | try { | 346 | join(); |
| 347 | join(); | 347 | } catch (InterruptedException ex) { |
| 348 | } catch (InterruptedException ex) { | 348 | throw new Error(ex); |
| 349 | throw new Error(ex); | 349 | } |
| 350 | } | 350 | } |
| 351 | } | 351 | |
| 352 | 352 | @Override | |
| 353 | @Override | 353 | public void run() { |
| 354 | public void run() { | 354 | deobfuscator = new Deobfuscator(jarFile); |
| 355 | deobfuscator = new Deobfuscator(jarFile); | 355 | } |
| 356 | } | 356 | } |
| 357 | } | ||
| 358 | } \ No newline at end of file | 357 | } \ No newline at end of file |
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 2602abcc..934d02a5 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import com.google.common.base.Charsets; | 14 | import com.google.common.base.Charsets; |
| @@ -40,587 +41,583 @@ import java.util.jar.JarOutputStream; | |||
| 40 | 41 | ||
| 41 | public class Deobfuscator { | 42 | public class Deobfuscator { |
| 42 | 43 | ||
| 43 | public interface ProgressListener { | 44 | private final JarFile jar; |
| 44 | void init(int totalWork, String title); | 45 | private final DecompilerSettings settings; |
| 45 | 46 | private final JarIndex jarIndex; | |
| 46 | void onProgress(int numDone, String message); | 47 | private final MappingsRenamer renamer; |
| 47 | } | 48 | private final Map<TranslationDirection, Translator> translatorCache; |
| 48 | 49 | private Mappings mappings; | |
| 49 | private final JarFile jar; | 50 | public Deobfuscator(JarFile jar) { |
| 50 | private final DecompilerSettings settings; | 51 | this.jar = jar; |
| 51 | private final JarIndex jarIndex; | 52 | |
| 52 | private final MappingsRenamer renamer; | 53 | // build the jar index |
| 53 | private final Map<TranslationDirection, Translator> translatorCache; | 54 | this.jarIndex = new JarIndex(); |
| 54 | private Mappings mappings; | 55 | this.jarIndex.indexJar(this.jar, true); |
| 55 | 56 | ||
| 56 | public Deobfuscator(JarFile jar) { | 57 | // config the decompiler |
| 57 | this.jar = jar; | 58 | this.settings = DecompilerSettings.javaDefaults(); |
| 58 | 59 | this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); | |
| 59 | // build the jar index | 60 | this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); |
| 60 | this.jarIndex = new JarIndex(); | 61 | this.settings.setForceExplicitTypeArguments( |
| 61 | this.jarIndex.indexJar(this.jar, true); | 62 | Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); |
| 62 | 63 | // DEBUG | |
| 63 | // config the decompiler | 64 | this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); |
| 64 | this.settings = DecompilerSettings.javaDefaults(); | 65 | this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); |
| 65 | this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); | 66 | |
| 66 | this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); | 67 | // init defaults |
| 67 | this.settings.setForceExplicitTypeArguments( | 68 | this.translatorCache = Maps.newTreeMap(); |
| 68 | Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); | 69 | this.renamer = new MappingsRenamer(this.jarIndex, null); |
| 69 | // DEBUG | 70 | // init mappings |
| 70 | this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); | 71 | setMappings(new Mappings()); |
| 71 | this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); | 72 | } |
| 72 | 73 | ||
| 73 | // init defaults | 74 | public JarFile getJar() { |
| 74 | this.translatorCache = Maps.newTreeMap(); | 75 | return this.jar; |
| 75 | this.renamer = new MappingsRenamer(this.jarIndex, null); | 76 | } |
| 76 | // init mappings | 77 | |
| 77 | setMappings(new Mappings()); | 78 | public String getJarName() { |
| 78 | } | 79 | return this.jar.getName(); |
| 79 | 80 | } | |
| 80 | public JarFile getJar() { | 81 | |
| 81 | return this.jar; | 82 | public JarIndex getJarIndex() { |
| 82 | } | 83 | return this.jarIndex; |
| 83 | 84 | } | |
| 84 | public String getJarName() { | 85 | |
| 85 | return this.jar.getName(); | 86 | public Mappings getMappings() { |
| 86 | } | 87 | return this.mappings; |
| 87 | 88 | } | |
| 88 | public JarIndex getJarIndex() { | 89 | |
| 89 | return this.jarIndex; | 90 | public void setMappings(Mappings val) { |
| 90 | } | 91 | setMappings(val, true); |
| 91 | 92 | } | |
| 92 | public Mappings getMappings() { | 93 | |
| 93 | return this.mappings; | 94 | public void setMappings(Mappings val, boolean warnAboutDrops) { |
| 94 | } | 95 | if (val == null) { |
| 95 | 96 | val = new Mappings(); | |
| 96 | public void setMappings(Mappings val) { | 97 | } |
| 97 | setMappings(val, true); | 98 | |
| 98 | } | 99 | // drop mappings that don't match the jar |
| 99 | 100 | MappingsChecker checker = new MappingsChecker(this.jarIndex); | |
| 100 | public void setMappings(Mappings val, boolean warnAboutDrops) { | 101 | checker.dropBrokenMappings(val); |
| 101 | if (val == null) { | 102 | if (warnAboutDrops) { |
| 102 | val = new Mappings(); | 103 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { |
| 103 | } | 104 | System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); |
| 104 | 105 | } | |
| 105 | // drop mappings that don't match the jar | 106 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { |
| 106 | MappingsChecker checker = new MappingsChecker(this.jarIndex); | 107 | System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); |
| 107 | checker.dropBrokenMappings(val); | 108 | } |
| 108 | if (warnAboutDrops) { | 109 | for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { |
| 109 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) { | 110 | System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); |
| 110 | System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); | 111 | } |
| 111 | } | 112 | for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { |
| 112 | for (java.util.Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) { | 113 | System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); |
| 113 | System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); | 114 | } |
| 114 | } | 115 | } |
| 115 | for (java.util.Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) { | 116 | |
| 116 | System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); | 117 | this.mappings = val; |
| 117 | } | 118 | this.renamer.setMappings(mappings); |
| 118 | for (java.util.Map.Entry<BehaviorEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) { | 119 | this.translatorCache.clear(); |
| 119 | System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); | 120 | } |
| 120 | } | 121 | |
| 121 | } | 122 | public Translator getTranslator(TranslationDirection direction) { |
| 122 | 123 | return this.translatorCache.computeIfAbsent(direction, | |
| 123 | this.mappings = val; | 124 | k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); |
| 124 | this.renamer.setMappings(mappings); | 125 | } |
| 125 | this.translatorCache.clear(); | 126 | |
| 126 | } | 127 | public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { |
| 127 | 128 | for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { | |
| 128 | public Translator getTranslator(TranslationDirection direction) { | 129 | // skip inner classes |
| 129 | return this.translatorCache.computeIfAbsent(direction, | 130 | if (obfClassEntry.isInnerClass()) { |
| 130 | k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); | 131 | continue; |
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { | 134 | // separate the classes |
| 134 | for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { | 135 | ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); |
| 135 | // skip inner classes | 136 | if (!deobfClassEntry.equals(obfClassEntry)) { |
| 136 | if (obfClassEntry.isInnerClass()) { | 137 | // if the class has a mapping, clearly it's deobfuscated |
| 137 | continue; | 138 | deobfClasses.add(deobfClassEntry); |
| 138 | } | 139 | } else if (obfClassEntry.getPackageName() != null) { |
| 139 | 140 | // also call it deobufscated if it's not in the none package | |
| 140 | // separate the classes | 141 | deobfClasses.add(obfClassEntry); |
| 141 | ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); | 142 | } else { |
| 142 | if (!deobfClassEntry.equals(obfClassEntry)) { | 143 | // otherwise, assume it's still obfuscated |
| 143 | // if the class has a mapping, clearly it's deobfuscated | 144 | obfClasses.add(obfClassEntry); |
| 144 | deobfClasses.add(deobfClassEntry); | 145 | } |
| 145 | } else if (obfClassEntry.getPackageName() != null) { | 146 | } |
| 146 | // also call it deobufscated if it's not in the none package | 147 | } |
| 147 | deobfClasses.add(obfClassEntry); | 148 | |
| 148 | } else { | 149 | public TranslatingTypeLoader createTypeLoader() { |
| 149 | // otherwise, assume it's still obfuscated | 150 | return new TranslatingTypeLoader( |
| 150 | obfClasses.add(obfClassEntry); | 151 | this.jar, |
| 151 | } | 152 | this.jarIndex, |
| 152 | } | 153 | getTranslator(TranslationDirection.Obfuscating), |
| 153 | } | 154 | getTranslator(TranslationDirection.Deobfuscating) |
| 154 | 155 | ); | |
| 155 | public TranslatingTypeLoader createTypeLoader() | 156 | } |
| 156 | { | 157 | |
| 157 | return new TranslatingTypeLoader( | 158 | public CompilationUnit getSourceTree(String className) { |
| 158 | this.jar, | 159 | |
| 159 | this.jarIndex, | 160 | // we don't know if this class name is obfuscated or deobfuscated |
| 160 | getTranslator(TranslationDirection.Obfuscating), | 161 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out |
| 161 | getTranslator(TranslationDirection.Deobfuscating) | 162 | // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one |
| 162 | ); | 163 | |
| 163 | } | 164 | // first, assume class name is deobf |
| 164 | 165 | String deobfClassName = className; | |
| 165 | public CompilationUnit getSourceTree(String className) { | 166 | |
| 166 | 167 | // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name | |
| 167 | // we don't know if this class name is obfuscated or deobfuscated | 168 | ClassMapping classMapping = this.mappings.getClassByObf(className); |
| 168 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out | 169 | if (classMapping != null && classMapping.getDeobfName() != null) { |
| 169 | // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one | 170 | deobfClassName = classMapping.getDeobfName(); |
| 170 | 171 | } | |
| 171 | // first, assume class name is deobf | 172 | |
| 172 | String deobfClassName = className; | 173 | // set the type loader |
| 173 | 174 | TranslatingTypeLoader loader = createTypeLoader(); | |
| 174 | // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name | 175 | this.settings.setTypeLoader(loader); |
| 175 | ClassMapping classMapping = this.mappings.getClassByObf(className); | 176 | |
| 176 | if (classMapping != null && classMapping.getDeobfName() != null) { | 177 | // see if procyon can find the type |
| 177 | deobfClassName = classMapping.getDeobfName(); | 178 | TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); |
| 178 | } | 179 | if (type == null) { |
| 179 | 180 | throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", | |
| 180 | // set the type loader | 181 | className, deobfClassName, loader.getClassNamesToTry(deobfClassName) |
| 181 | TranslatingTypeLoader loader = createTypeLoader(); | 182 | )); |
| 182 | this.settings.setTypeLoader(loader); | 183 | } |
| 183 | 184 | TypeDefinition resolvedType = type.resolve(); | |
| 184 | // see if procyon can find the type | 185 | |
| 185 | TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); | 186 | // decompile it! |
| 186 | if (type == null) { | 187 | DecompilerContext context = new DecompilerContext(); |
| 187 | throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", | 188 | context.setCurrentType(resolvedType); |
| 188 | className, deobfClassName, loader.getClassNamesToTry(deobfClassName) | 189 | context.setSettings(this.settings); |
| 189 | )); | 190 | AstBuilder builder = new AstBuilder(context); |
| 190 | } | 191 | builder.addType(resolvedType); |
| 191 | TypeDefinition resolvedType = type.resolve(); | 192 | builder.runTransformations(null); |
| 192 | 193 | return builder.getCompilationUnit(); | |
| 193 | // decompile it! | 194 | } |
| 194 | DecompilerContext context = new DecompilerContext(); | 195 | |
| 195 | context.setCurrentType(resolvedType); | 196 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { |
| 196 | context.setSettings(this.settings); | 197 | return getSourceIndex(sourceTree, source, null); |
| 197 | AstBuilder builder = new AstBuilder(context); | 198 | } |
| 198 | builder.addType(resolvedType); | 199 | |
| 199 | builder.runTransformations(null); | 200 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { |
| 200 | return builder.getCompilationUnit(); | 201 | |
| 201 | } | 202 | // build the source index |
| 202 | 203 | SourceIndex index; | |
| 203 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { | 204 | if (ignoreBadTokens != null) { |
| 204 | return getSourceIndex(sourceTree, source, null); | 205 | index = new SourceIndex(source, ignoreBadTokens); |
| 205 | } | 206 | } else { |
| 206 | 207 | index = new SourceIndex(source); | |
| 207 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { | 208 | } |
| 208 | 209 | sourceTree.acceptVisitor(new SourceIndexVisitor(), index); | |
| 209 | // build the source index | 210 | |
| 210 | SourceIndex index; | 211 | // DEBUG |
| 211 | if (ignoreBadTokens != null) { | 212 | // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); |
| 212 | index = new SourceIndex(source, ignoreBadTokens); | 213 | |
| 213 | } else { | 214 | // resolve all the classes in the source references |
| 214 | index = new SourceIndex(source); | 215 | for (Token token : index.referenceTokens()) { |
| 215 | } | 216 | EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token); |
| 216 | sourceTree.acceptVisitor(new SourceIndexVisitor(), index); | 217 | |
| 217 | 218 | // get the obfuscated entry | |
| 218 | // DEBUG | 219 | Entry obfEntry = obfuscateEntry(deobfReference.entry); |
| 219 | // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); | 220 | |
| 220 | 221 | // try to resolve the class | |
| 221 | // resolve all the classes in the source references | 222 | ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); |
| 222 | for (Token token : index.referenceTokens()) { | 223 | if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { |
| 223 | EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token); | 224 | // change the class of the entry |
| 224 | 225 | obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); | |
| 225 | // get the obfuscated entry | 226 | |
| 226 | Entry obfEntry = obfuscateEntry(deobfReference.entry); | 227 | // save the new deobfuscated reference |
| 227 | 228 | deobfReference.entry = deobfuscateEntry(obfEntry); | |
| 228 | // try to resolve the class | 229 | index.replaceDeobfReference(token, deobfReference); |
| 229 | ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); | 230 | } |
| 230 | if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { | 231 | |
| 231 | // change the class of the entry | 232 | // DEBUG |
| 232 | obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); | 233 | // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); |
| 233 | 234 | } | |
| 234 | // save the new deobfuscated reference | 235 | |
| 235 | deobfReference.entry = deobfuscateEntry(obfEntry); | 236 | return index; |
| 236 | index.replaceDeobfReference(token, deobfReference); | 237 | } |
| 237 | } | 238 | |
| 238 | 239 | public String getSource(CompilationUnit sourceTree) { | |
| 239 | // DEBUG | 240 | // render the AST into source |
| 240 | // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); | 241 | StringWriter buf = new StringWriter(); |
| 241 | } | 242 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); |
| 242 | 243 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); | |
| 243 | return index; | 244 | return buf.toString(); |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 246 | public String getSource(CompilationUnit sourceTree) { | 247 | public void writeSources(File dirOut, ProgressListener progress) { |
| 247 | // render the AST into source | 248 | // get the classes to decompile |
| 248 | StringWriter buf = new StringWriter(); | 249 | Set<ClassEntry> classEntries = Sets.newHashSet(); |
| 249 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | 250 | for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { |
| 250 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); | 251 | // skip inner classes |
| 251 | return buf.toString(); | 252 | if (obfClassEntry.isInnerClass()) { |
| 252 | } | 253 | continue; |
| 253 | 254 | } | |
| 254 | public void writeSources(File dirOut, ProgressListener progress) { | 255 | |
| 255 | // get the classes to decompile | 256 | classEntries.add(obfClassEntry); |
| 256 | Set<ClassEntry> classEntries = Sets.newHashSet(); | 257 | } |
| 257 | for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { | 258 | |
| 258 | // skip inner classes | 259 | if (progress != null) { |
| 259 | if (obfClassEntry.isInnerClass()) { | 260 | progress.init(classEntries.size(), "Decompiling classes..."); |
| 260 | continue; | 261 | } |
| 261 | } | 262 | |
| 262 | 263 | // DEOBFUSCATE ALL THE THINGS!! @_@ | |
| 263 | classEntries.add(obfClassEntry); | 264 | int i = 0; |
| 264 | } | 265 | for (ClassEntry obfClassEntry : classEntries) { |
| 265 | 266 | ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); | |
| 266 | if (progress != null) { | 267 | if (progress != null) { |
| 267 | progress.init(classEntries.size(), "Decompiling classes..."); | 268 | progress.onProgress(i++, deobfClassEntry.toString()); |
| 268 | } | 269 | } |
| 269 | 270 | ||
| 270 | // DEOBFUSCATE ALL THE THINGS!! @_@ | 271 | try { |
| 271 | int i = 0; | 272 | // get the source |
| 272 | for (ClassEntry obfClassEntry : classEntries) { | 273 | String source = getSource(getSourceTree(obfClassEntry.getName())); |
| 273 | ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); | 274 | |
| 274 | if (progress != null) { | 275 | // write the file |
| 275 | progress.onProgress(i++, deobfClassEntry.toString()); | 276 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); |
| 276 | } | 277 | file.getParentFile().mkdirs(); |
| 277 | 278 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { | |
| 278 | try { | 279 | out.write(source); |
| 279 | // get the source | 280 | } |
| 280 | String source = getSource(getSourceTree(obfClassEntry.getName())); | 281 | } catch (Throwable t) { |
| 281 | 282 | // don't crash the whole world here, just log the error and keep going | |
| 282 | // write the file | 283 | // TODO: set up logback via log4j |
| 283 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); | 284 | System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")"); |
| 284 | file.getParentFile().mkdirs(); | 285 | t.printStackTrace(System.err); |
| 285 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) { | 286 | } |
| 286 | out.write(source); | 287 | } |
| 287 | } | 288 | if (progress != null) { |
| 288 | } catch (Throwable t) { | 289 | progress.onProgress(i, "Done!"); |
| 289 | // don't crash the whole world here, just log the error and keep going | 290 | } |
| 290 | // TODO: set up logback via log4j | 291 | } |
| 291 | System.err.println("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")"); | 292 | |
| 292 | t.printStackTrace(System.err); | 293 | private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) { |
| 293 | } | 294 | for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) { |
| 294 | } | 295 | if (classEntries.add(interfaceEntry)) { |
| 295 | if (progress != null) { | 296 | addAllPotentialAncestors(classEntries, interfaceEntry); |
| 296 | progress.onProgress(i, "Done!"); | 297 | } |
| 297 | } | 298 | } |
| 298 | } | 299 | |
| 299 | 300 | ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry); | |
| 300 | private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) { | 301 | if (superClassEntry != null && classEntries.add(superClassEntry)) { |
| 301 | for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) { | 302 | addAllPotentialAncestors(classEntries, superClassEntry); |
| 302 | if (classEntries.add(interfaceEntry)) { | 303 | } |
| 303 | addAllPotentialAncestors(classEntries, interfaceEntry); | 304 | } |
| 304 | } | 305 | |
| 305 | } | 306 | private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { |
| 306 | 307 | if (behaviorEntry instanceof MethodEntry) { | |
| 307 | ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry); | 308 | MethodEntry methodEntry = (MethodEntry) behaviorEntry; |
| 308 | if (superClassEntry != null && classEntries.add(superClassEntry)) { | 309 | |
| 309 | addAllPotentialAncestors(classEntries, superClassEntry); | 310 | Set<ClassEntry> classEntries = new HashSet<>(); |
| 310 | } | 311 | addAllPotentialAncestors(classEntries, classObfEntry); |
| 311 | } | 312 | |
| 312 | 313 | for (ClassEntry parentEntry : classEntries) { | |
| 313 | private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { | 314 | MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); |
| 314 | if (behaviorEntry instanceof MethodEntry) { | 315 | if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { |
| 315 | MethodEntry methodEntry = (MethodEntry) behaviorEntry; | 316 | return false; |
| 316 | 317 | } | |
| 317 | Set<ClassEntry> classEntries = new HashSet<>(); | 318 | } |
| 318 | addAllPotentialAncestors(classEntries, classObfEntry); | 319 | } |
| 319 | 320 | ||
| 320 | for (ClassEntry parentEntry : classEntries) { | 321 | return true; |
| 321 | MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); | 322 | } |
| 322 | if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { | 323 | |
| 323 | return false; | 324 | public void rebuildMethodNames(ProgressListener progress) { |
| 324 | } | 325 | int i = 0; |
| 325 | } | 326 | Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>(); |
| 326 | } | 327 | |
| 327 | 328 | progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); | |
| 328 | return true; | 329 | |
| 329 | } | 330 | for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { |
| 330 | 331 | Map<Entry, String> renameEntries = new HashMap<>(); | |
| 331 | public void rebuildMethodNames(ProgressListener progress) { | 332 | |
| 332 | int i = 0; | 333 | progress.onProgress(i++, classMapping.getDeobfName()); |
| 333 | Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>(); | 334 | |
| 334 | 335 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { | |
| 335 | progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); | 336 | ClassEntry classObfEntry = classMapping.getObfEntry(); |
| 336 | 337 | BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); | |
| 337 | for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { | 338 | |
| 338 | Map<Entry, String> renameEntries = new HashMap<>(); | 339 | if (isBehaviorProvider(classObfEntry, obfEntry)) { |
| 339 | 340 | if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) | |
| 340 | progress.onProgress(i++, classMapping.getDeobfName()); | 341 | && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { |
| 341 | 342 | renameEntries.put(obfEntry, methodMapping.getDeobfName()); | |
| 342 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { | 343 | } |
| 343 | ClassEntry classObfEntry = classMapping.getObfEntry(); | 344 | |
| 344 | BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); | 345 | for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { |
| 345 | 346 | Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); | |
| 346 | if (isBehaviorProvider(classObfEntry, obfEntry)) { | 347 | if (hasDeobfuscatedName(argObfEntry)) { |
| 347 | if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) | 348 | renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); |
| 348 | && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { | 349 | } |
| 349 | renameEntries.put(obfEntry, methodMapping.getDeobfName()); | 350 | } |
| 350 | } | 351 | } |
| 351 | 352 | } | |
| 352 | for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { | 353 | |
| 353 | Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); | 354 | renameClassMap.put(classMapping, renameEntries); |
| 354 | if (hasDeobfuscatedName(argObfEntry)) { | 355 | } |
| 355 | renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); | 356 | |
| 356 | } | 357 | for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { |
| 357 | } | 358 | progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); |
| 358 | } | 359 | |
| 359 | } | 360 | for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { |
| 360 | 361 | Entry obfEntry = entry.getKey(); | |
| 361 | renameClassMap.put(classMapping, renameEntries); | 362 | |
| 362 | } | 363 | removeMapping(obfEntry); |
| 363 | 364 | } | |
| 364 | for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { | 365 | } |
| 365 | progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); | 366 | |
| 366 | 367 | for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { | |
| 367 | for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { | 368 | progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); |
| 368 | Entry obfEntry = entry.getKey(); | 369 | |
| 369 | 370 | for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { | |
| 370 | removeMapping(obfEntry); | 371 | Entry obfEntry = entry.getKey(); |
| 371 | } | 372 | String name = entry.getValue(); |
| 372 | } | 373 | |
| 373 | 374 | rename(obfEntry, name); | |
| 374 | for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { | 375 | } |
| 375 | progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); | 376 | } |
| 376 | 377 | } | |
| 377 | for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { | 378 | |
| 378 | Entry obfEntry = entry.getKey(); | 379 | public void writeJar(File out, ProgressListener progress) { |
| 379 | String name = entry.getValue(); | 380 | transformJar(out, progress, createTypeLoader()::transformClass); |
| 380 | 381 | } | |
| 381 | rename(obfEntry, name); | 382 | |
| 382 | } | 383 | public void protectifyJar(File out, ProgressListener progress) { |
| 383 | } | 384 | transformJar(out, progress, ClassProtectifier::protectify); |
| 384 | } | 385 | } |
| 385 | 386 | ||
| 386 | public void writeJar(File out, ProgressListener progress) { | 387 | public void publifyJar(File out, ProgressListener progress) { |
| 387 | transformJar(out, progress, createTypeLoader()::transformClass); | 388 | transformJar(out, progress, ClassPublifier::publify); |
| 388 | } | 389 | } |
| 389 | 390 | ||
| 390 | public void protectifyJar(File out, ProgressListener progress) { | 391 | public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { |
| 391 | transformJar(out, progress, ClassProtectifier::protectify); | 392 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { |
| 392 | } | 393 | if (progress != null) { |
| 393 | 394 | progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); | |
| 394 | public void publifyJar(File out, ProgressListener progress) { | 395 | } |
| 395 | transformJar(out, progress, ClassPublifier::publify); | 396 | |
| 396 | } | 397 | int i = 0; |
| 397 | 398 | for (CtClass c : JarClassIterator.classes(this.jar)) { | |
| 398 | public interface ClassTransformer { | 399 | if (progress != null) { |
| 399 | CtClass transform(CtClass c) throws Exception; | 400 | progress.onProgress(i++, c.getName()); |
| 400 | } | 401 | } |
| 401 | 402 | ||
| 402 | public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { | 403 | try { |
| 403 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { | 404 | c = transformer.transform(c); |
| 404 | if (progress != null) { | 405 | outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); |
| 405 | progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); | 406 | outJar.write(c.toBytecode()); |
| 406 | } | 407 | outJar.closeEntry(); |
| 407 | 408 | } catch (Throwable t) { | |
| 408 | int i = 0; | 409 | throw new Error("Unable to transform class " + c.getName(), t); |
| 409 | for (CtClass c : JarClassIterator.classes(this.jar)) { | 410 | } |
| 410 | if (progress != null) { | 411 | } |
| 411 | progress.onProgress(i++, c.getName()); | 412 | if (progress != null) { |
| 412 | } | 413 | progress.onProgress(i, "Done!"); |
| 413 | 414 | } | |
| 414 | try { | 415 | |
| 415 | c = transformer.transform(c); | 416 | outJar.close(); |
| 416 | outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); | 417 | } catch (IOException ex) { |
| 417 | outJar.write(c.toBytecode()); | 418 | throw new Error("Unable to write to Jar file!"); |
| 418 | outJar.closeEntry(); | 419 | } |
| 419 | } catch (Throwable t) { | 420 | } |
| 420 | throw new Error("Unable to transform class " + c.getName(), t); | 421 | |
| 421 | } | 422 | public <T extends Entry> T obfuscateEntry(T deobfEntry) { |
| 422 | } | 423 | if (deobfEntry == null) { |
| 423 | if (progress != null) { | 424 | return null; |
| 424 | progress.onProgress(i, "Done!"); | 425 | } |
| 425 | } | 426 | return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); |
| 426 | 427 | } | |
| 427 | outJar.close(); | 428 | |
| 428 | } catch (IOException ex) { | 429 | public <T extends Entry> T deobfuscateEntry(T obfEntry) { |
| 429 | throw new Error("Unable to write to Jar file!"); | 430 | if (obfEntry == null) { |
| 430 | } | 431 | return null; |
| 431 | } | 432 | } |
| 432 | 433 | return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); | |
| 433 | public <T extends Entry> T obfuscateEntry(T deobfEntry) { | 434 | } |
| 434 | if (deobfEntry == null) { | 435 | |
| 435 | return null; | 436 | public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { |
| 436 | } | 437 | if (deobfReference == null) { |
| 437 | return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); | 438 | return null; |
| 438 | } | 439 | } |
| 439 | 440 | return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); | |
| 440 | public <T extends Entry> T deobfuscateEntry(T obfEntry) { | 441 | } |
| 441 | if (obfEntry == null) { | 442 | |
| 442 | return null; | 443 | public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { |
| 443 | } | 444 | if (obfReference == null) { |
| 444 | return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); | 445 | return null; |
| 445 | } | 446 | } |
| 446 | 447 | return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference); | |
| 447 | public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) { | 448 | } |
| 448 | if (deobfReference == null) { | 449 | |
| 449 | return null; | 450 | public boolean isObfuscatedIdentifier(Entry obfEntry) { |
| 450 | } | 451 | return isObfuscatedIdentifier(obfEntry, false); |
| 451 | return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); | 452 | } |
| 452 | } | 453 | |
| 453 | 454 | public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) { | |
| 454 | public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { | 455 | |
| 455 | if (obfReference == null) { | 456 | if (obfEntry instanceof MethodEntry) { |
| 456 | return null; | 457 | |
| 457 | } | 458 | // HACKHACK: Object methods are not obfuscated identifiers |
| 458 | return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference); | 459 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; |
| 459 | } | 460 | String name = obfMethodEntry.getName(); |
| 460 | 461 | String sig = obfMethodEntry.getSignature().toString(); | |
| 461 | public boolean isObfuscatedIdentifier(Entry obfEntry) { | 462 | if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { |
| 462 | return isObfuscatedIdentifier(obfEntry, false); | 463 | return false; |
| 463 | } | 464 | } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { |
| 464 | 465 | return false; | |
| 465 | public boolean isObfuscatedIdentifier(Entry obfEntry, boolean hack) { | 466 | } else if (name.equals("finalize") && sig.equals("()V")) { |
| 466 | 467 | return false; | |
| 467 | if (obfEntry instanceof MethodEntry) { | 468 | } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) { |
| 468 | 469 | return false; | |
| 469 | // HACKHACK: Object methods are not obfuscated identifiers | 470 | } else if (name.equals("hashCode") && sig.equals("()I")) { |
| 470 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; | 471 | return false; |
| 471 | String name = obfMethodEntry.getName(); | 472 | } else if (name.equals("notify") && sig.equals("()V")) { |
| 472 | String sig = obfMethodEntry.getSignature().toString(); | 473 | return false; |
| 473 | if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { | 474 | } else if (name.equals("notifyAll") && sig.equals("()V")) { |
| 474 | return false; | 475 | return false; |
| 475 | } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { | 476 | } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) { |
| 476 | return false; | 477 | return false; |
| 477 | } else if (name.equals("finalize") && sig.equals("()V")) { | 478 | } else if (name.equals("wait") && sig.equals("()V")) { |
| 478 | return false; | 479 | return false; |
| 479 | } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) { | 480 | } else if (name.equals("wait") && sig.equals("(J)V")) { |
| 480 | return false; | 481 | return false; |
| 481 | } else if (name.equals("hashCode") && sig.equals("()I")) { | 482 | } else if (name.equals("wait") && sig.equals("(JI)V")) { |
| 482 | return false; | 483 | return false; |
| 483 | } else if (name.equals("notify") && sig.equals("()V")) { | 484 | } |
| 484 | return false; | 485 | |
| 485 | } else if (name.equals("notifyAll") && sig.equals("()V")) { | 486 | // FIXME: HACK EVEN MORE HACK! |
| 486 | return false; | 487 | if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) |
| 487 | } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) { | 488 | return true; |
| 488 | return false; | 489 | } |
| 489 | } else if (name.equals("wait") && sig.equals("()V")) { | 490 | |
| 490 | return false; | 491 | return this.jarIndex.containsObfEntry(obfEntry); |
| 491 | } else if (name.equals("wait") && sig.equals("(J)V")) { | 492 | } |
| 492 | return false; | 493 | |
| 493 | } else if (name.equals("wait") && sig.equals("(JI)V")) { | 494 | public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) { |
| 494 | return false; | 495 | return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack); |
| 495 | } | 496 | } |
| 496 | 497 | ||
| 497 | // FIXME: HACK EVEN MORE HACK! | 498 | public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { |
| 498 | if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) | 499 | return isRenameable(obfReference, false); |
| 499 | return true; | 500 | } |
| 500 | } | 501 | |
| 501 | 502 | public boolean hasDeobfuscatedName(Entry obfEntry) { | |
| 502 | return this.jarIndex.containsObfEntry(obfEntry); | 503 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); |
| 503 | } | 504 | if (obfEntry instanceof ClassEntry) { |
| 504 | 505 | ClassEntry obfClass = (ClassEntry) obfEntry; | |
| 505 | public boolean isRenameable(EntryReference<Entry, Entry> obfReference, boolean activeHack) { | 506 | List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); |
| 506 | return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry(), activeHack); | 507 | ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); |
| 507 | } | 508 | return classMapping != null && classMapping.getDeobfName() != null; |
| 508 | 509 | } else if (obfEntry instanceof FieldEntry) { | |
| 509 | public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { | 510 | return translator.translate((FieldEntry) obfEntry) != null; |
| 510 | return isRenameable(obfReference, false); | 511 | } else if (obfEntry instanceof MethodEntry) { |
| 511 | } | 512 | return translator.translate((MethodEntry) obfEntry) != null; |
| 512 | 513 | } else if (obfEntry instanceof ConstructorEntry) { | |
| 513 | // NOTE: these methods are a bit messy... oh well | 514 | // constructors have no names |
| 514 | 515 | return false; | |
| 515 | public boolean hasDeobfuscatedName(Entry obfEntry) { | 516 | } else if (obfEntry instanceof ArgumentEntry) { |
| 516 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); | 517 | return translator.translate((ArgumentEntry) obfEntry) != null; |
| 517 | if (obfEntry instanceof ClassEntry) { | 518 | } else if (obfEntry instanceof LocalVariableEntry) { |
| 518 | ClassEntry obfClass = (ClassEntry) obfEntry; | 519 | // TODO: Implement it |
| 519 | List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass); | 520 | //return translator.translate((LocalVariableEntry)obfEntry) != null; |
| 520 | ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); | 521 | return false; |
| 521 | return classMapping != null && classMapping.getDeobfName() != null; | 522 | } else { |
| 522 | } else if (obfEntry instanceof FieldEntry) { | 523 | throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); |
| 523 | return translator.translate((FieldEntry) obfEntry) != null; | 524 | } |
| 524 | } else if (obfEntry instanceof MethodEntry) { | 525 | } |
| 525 | return translator.translate((MethodEntry) obfEntry) != null; | 526 | |
| 526 | } else if (obfEntry instanceof ConstructorEntry) { | 527 | public void rename(Entry obfEntry, String newName) { |
| 527 | // constructors have no names | 528 | rename(obfEntry, newName, true); |
| 528 | return false; | 529 | } |
| 529 | } else if (obfEntry instanceof ArgumentEntry) { | 530 | |
| 530 | return translator.translate((ArgumentEntry) obfEntry) != null; | 531 | // NOTE: these methods are a bit messy... oh well |
| 531 | } else if (obfEntry instanceof LocalVariableEntry) { | 532 | |
| 532 | // TODO: Implement it | 533 | public void rename(Entry obfEntry, String newName, boolean clearCache) { |
| 533 | //return translator.translate((LocalVariableEntry)obfEntry) != null; | 534 | if (obfEntry instanceof ClassEntry) { |
| 534 | return false; | 535 | this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); |
| 535 | } else { | 536 | } else if (obfEntry instanceof FieldEntry) { |
| 536 | throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); | 537 | this.renamer.setFieldName((FieldEntry) obfEntry, newName); |
| 537 | } | 538 | } else if (obfEntry instanceof MethodEntry) { |
| 538 | } | 539 | this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); |
| 539 | 540 | } else if (obfEntry instanceof ConstructorEntry) { | |
| 540 | public void rename(Entry obfEntry, String newName) { | 541 | throw new IllegalArgumentException("Cannot rename constructors"); |
| 541 | rename(obfEntry, newName, true); | 542 | } else if (obfEntry instanceof ArgumentEntry) { |
| 542 | } | 543 | this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); |
| 543 | 544 | } else if (obfEntry instanceof LocalVariableEntry) { | |
| 544 | public void rename(Entry obfEntry, String newName, boolean clearCache) { | 545 | // TODO: Implement it |
| 545 | if (obfEntry instanceof ClassEntry) { | 546 | } else { |
| 546 | this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); | 547 | throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); |
| 547 | } else if (obfEntry instanceof FieldEntry) { | 548 | } |
| 548 | this.renamer.setFieldName((FieldEntry) obfEntry, newName); | 549 | |
| 549 | } else if (obfEntry instanceof MethodEntry) { | 550 | // clear caches |
| 550 | this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); | 551 | if (clearCache) |
| 551 | } else if (obfEntry instanceof ConstructorEntry) { | 552 | this.translatorCache.clear(); |
| 552 | throw new IllegalArgumentException("Cannot rename constructors"); | 553 | } |
| 553 | } else if (obfEntry instanceof ArgumentEntry) { | 554 | |
| 554 | this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); | 555 | public void removeMapping(Entry obfEntry) { |
| 555 | } else if (obfEntry instanceof LocalVariableEntry) { | 556 | if (obfEntry instanceof ClassEntry) { |
| 556 | // TODO: Implement it | 557 | this.renamer.removeClassMapping((ClassEntry) obfEntry); |
| 557 | } else { | 558 | } else if (obfEntry instanceof FieldEntry) { |
| 558 | throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); | 559 | this.renamer.removeFieldMapping((FieldEntry) obfEntry); |
| 559 | } | 560 | } else if (obfEntry instanceof MethodEntry) { |
| 560 | 561 | this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); | |
| 561 | // clear caches | 562 | } else if (obfEntry instanceof ConstructorEntry) { |
| 562 | if (clearCache) | 563 | throw new IllegalArgumentException("Cannot rename constructors"); |
| 563 | this.translatorCache.clear(); | 564 | } else if (obfEntry instanceof ArgumentEntry) { |
| 564 | } | 565 | this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry); |
| 565 | 566 | } else { | |
| 566 | public void removeMapping(Entry obfEntry) { | 567 | throw new Error("Unknown entry type: " + obfEntry); |
| 567 | if (obfEntry instanceof ClassEntry) { | 568 | } |
| 568 | this.renamer.removeClassMapping((ClassEntry) obfEntry); | 569 | |
| 569 | } else if (obfEntry instanceof FieldEntry) { | 570 | // clear caches |
| 570 | this.renamer.removeFieldMapping((FieldEntry) obfEntry); | 571 | this.translatorCache.clear(); |
| 571 | } else if (obfEntry instanceof MethodEntry) { | 572 | } |
| 572 | this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); | 573 | |
| 573 | } else if (obfEntry instanceof ConstructorEntry) { | 574 | public void markAsDeobfuscated(Entry obfEntry) { |
| 574 | throw new IllegalArgumentException("Cannot rename constructors"); | 575 | if (obfEntry instanceof ClassEntry) { |
| 575 | } else if (obfEntry instanceof ArgumentEntry) { | 576 | this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry); |
| 576 | this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry); | 577 | } else if (obfEntry instanceof FieldEntry) { |
| 577 | } else { | 578 | this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); |
| 578 | throw new Error("Unknown entry type: " + obfEntry); | 579 | } else if (obfEntry instanceof MethodEntry) { |
| 579 | } | 580 | this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); |
| 580 | 581 | } else if (obfEntry instanceof ConstructorEntry) { | |
| 581 | // clear caches | 582 | throw new IllegalArgumentException("Cannot rename constructors"); |
| 582 | this.translatorCache.clear(); | 583 | } else if (obfEntry instanceof ArgumentEntry) { |
| 583 | } | 584 | this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); |
| 584 | 585 | } else if (obfEntry instanceof LocalVariableEntry) { | |
| 585 | public void markAsDeobfuscated(Entry obfEntry) { | 586 | // TODO: Implement it |
| 586 | if (obfEntry instanceof ClassEntry) { | 587 | } else { |
| 587 | this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry); | 588 | throw new Error("Unknown entry type: " + obfEntry); |
| 588 | } else if (obfEntry instanceof FieldEntry) { | 589 | } |
| 589 | this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); | 590 | |
| 590 | } else if (obfEntry instanceof MethodEntry) { | 591 | // clear caches |
| 591 | this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); | 592 | this.translatorCache.clear(); |
| 592 | } else if (obfEntry instanceof ConstructorEntry) { | 593 | } |
| 593 | throw new IllegalArgumentException("Cannot rename constructors"); | 594 | |
| 594 | } else if (obfEntry instanceof ArgumentEntry) { | 595 | public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) { |
| 595 | this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); | 596 | Entry obfEntry = obfuscateEntry(entry); |
| 596 | } else if (obfEntry instanceof LocalVariableEntry) { | 597 | if (obfEntry instanceof ClassEntry) |
| 597 | // TODO: Implement it | 598 | this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); |
| 598 | } else { | 599 | else if (obfEntry instanceof FieldEntry) |
| 599 | throw new Error("Unknown entry type: " + obfEntry); | 600 | this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); |
| 600 | } | 601 | else if (obfEntry instanceof BehaviorEntry) |
| 601 | 602 | this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); | |
| 602 | // clear caches | 603 | else |
| 603 | this.translatorCache.clear(); | 604 | throw new Error("Unknown entry type: " + obfEntry); |
| 604 | } | 605 | } |
| 605 | 606 | ||
| 606 | public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) | 607 | public Mappings.EntryModifier getModifier(Entry obEntry) { |
| 607 | { | 608 | Entry entry = obfuscateEntry(obEntry); |
| 608 | Entry obfEntry = obfuscateEntry(entry); | 609 | if (entry != null) |
| 609 | if (obfEntry instanceof ClassEntry) | 610 | obEntry = entry; |
| 610 | this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); | 611 | return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); |
| 611 | else if (obfEntry instanceof FieldEntry) | 612 | } |
| 612 | this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); | 613 | |
| 613 | else if (obfEntry instanceof BehaviorEntry) | 614 | public interface ProgressListener { |
| 614 | this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); | 615 | void init(int totalWork, String title); |
| 615 | else | 616 | |
| 616 | throw new Error("Unknown entry type: " + obfEntry); | 617 | void onProgress(int numDone, String message); |
| 617 | } | 618 | } |
| 618 | 619 | ||
| 619 | public Mappings.EntryModifier getModifier(Entry obEntry) | 620 | public interface ClassTransformer { |
| 620 | { | 621 | CtClass transform(CtClass c) throws Exception; |
| 621 | Entry entry = obfuscateEntry(obEntry); | 622 | } |
| 622 | if (entry != null) | ||
| 623 | obEntry = entry; | ||
| 624 | return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); | ||
| 625 | } | ||
| 626 | } | 623 | } |
diff --git a/src/main/java/cuchaz/enigma/ExceptionIgnorer.java b/src/main/java/cuchaz/enigma/ExceptionIgnorer.java index fc89fa6d..84331cc1 100644 --- a/src/main/java/cuchaz/enigma/ExceptionIgnorer.java +++ b/src/main/java/cuchaz/enigma/ExceptionIgnorer.java | |||
| @@ -8,27 +8,28 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | public class ExceptionIgnorer { | 14 | public class ExceptionIgnorer { |
| 14 | 15 | ||
| 15 | public static boolean shouldIgnore(Throwable t) { | 16 | public static boolean shouldIgnore(Throwable t) { |
| 16 | 17 | ||
| 17 | // is this that pesky concurrent access bug in the highlight painter system? | 18 | // is this that pesky concurrent access bug in the highlight painter system? |
| 18 | // (ancient ui code is ancient) | 19 | // (ancient ui code is ancient) |
| 19 | if (t instanceof ArrayIndexOutOfBoundsException) { | 20 | if (t instanceof ArrayIndexOutOfBoundsException) { |
| 20 | StackTraceElement[] stackTrace = t.getStackTrace(); | 21 | StackTraceElement[] stackTrace = t.getStackTrace(); |
| 21 | if (stackTrace.length > 1) { | 22 | if (stackTrace.length > 1) { |
| 22 | 23 | ||
| 23 | // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? | 24 | // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? |
| 24 | StackTraceElement frame = stackTrace[1]; | 25 | StackTraceElement frame = stackTrace[1]; |
| 25 | if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { | 26 | if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { |
| 26 | return true; | 27 | return true; |
| 27 | } | 28 | } |
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | return false; | 32 | return false; |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | } | 35 | } |
diff --git a/src/main/java/cuchaz/enigma/Main.java b/src/main/java/cuchaz/enigma/Main.java index 73709868..fa8d0622 100644 --- a/src/main/java/cuchaz/enigma/Main.java +++ b/src/main/java/cuchaz/enigma/Main.java | |||
| @@ -8,48 +8,48 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 14 | import cuchaz.enigma.gui.Gui; | ||
| 15 | |||
| 16 | import javax.swing.*; | ||
| 13 | import java.io.File; | 17 | import java.io.File; |
| 14 | import java.util.jar.JarFile; | 18 | import java.util.jar.JarFile; |
| 15 | 19 | ||
| 16 | import javax.swing.UIManager; | ||
| 17 | |||
| 18 | import cuchaz.enigma.gui.Gui; | ||
| 19 | |||
| 20 | public class Main { | 20 | public class Main { |
| 21 | 21 | ||
| 22 | public static void main(String[] args) throws Exception { | 22 | public static void main(String[] args) throws Exception { |
| 23 | if (System.getProperty("enigma.useSystemLookAndFeel", "true").equals("true")) | 23 | if (System.getProperty("enigma.useSystemLookAndFeel", "true").equals("true")) |
| 24 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | 24 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); |
| 25 | Gui gui = new Gui(); | 25 | Gui gui = new Gui(); |
| 26 | 26 | ||
| 27 | // parse command-line args | 27 | // parse command-line args |
| 28 | if (args.length >= 1) { | 28 | if (args.length >= 1) { |
| 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 | gui.getController().openEnigmaMappings(getFile(args[1])); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | // DEBUG | 35 | // DEBUG |
| 36 | //gui.getController().openDeclaration(new ClassEntry("none/byp")); | 36 | //gui.getController().openDeclaration(new ClassEntry("none/byp")); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | private static File getFile(String path) { | 39 | private static File getFile(String path) { |
| 40 | // expand ~ to the home dir | 40 | // expand ~ to the home dir |
| 41 | if (path.startsWith("~")) { | 41 | if (path.startsWith("~")) { |
| 42 | // get the home dir | 42 | // get the home dir |
| 43 | File dirHome = new File(System.getProperty("user.home")); | 43 | File dirHome = new File(System.getProperty("user.home")); |
| 44 | 44 | ||
| 45 | // is the path just ~/ or is it ~user/ ? | 45 | // is the path just ~/ or is it ~user/ ? |
| 46 | if (path.startsWith("~/")) { | 46 | if (path.startsWith("~/")) { |
| 47 | return new File(dirHome, path.substring(2)); | 47 | return new File(dirHome, path.substring(2)); |
| 48 | } else { | 48 | } else { |
| 49 | return new File(dirHome.getParentFile(), path.substring(1)); | 49 | return new File(dirHome.getParentFile(), path.substring(1)); |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | return new File(path); | 53 | return new File(path); |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index 73405662..7304f722 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java | |||
| @@ -8,14 +8,24 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 15 | |||
| 16 | import com.strobel.assembler.metadata.Buffer; | 16 | import com.strobel.assembler.metadata.Buffer; |
| 17 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | 17 | import com.strobel.assembler.metadata.ClasspathTypeLoader; |
| 18 | import com.strobel.assembler.metadata.ITypeLoader; | 18 | import com.strobel.assembler.metadata.ITypeLoader; |
| 19 | import cuchaz.enigma.analysis.BridgeMarker; | ||
| 20 | import cuchaz.enigma.analysis.JarIndex; | ||
| 21 | import cuchaz.enigma.bytecode.ClassTranslator; | ||
| 22 | import cuchaz.enigma.bytecode.InnerClassWriter; | ||
| 23 | import cuchaz.enigma.bytecode.LocalVariableRenamer; | ||
| 24 | import cuchaz.enigma.bytecode.MethodParameterWriter; | ||
| 25 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 26 | import cuchaz.enigma.mapping.Translator; | ||
| 27 | import javassist.*; | ||
| 28 | import javassist.bytecode.Descriptor; | ||
| 19 | 29 | ||
| 20 | import java.io.ByteArrayOutputStream; | 30 | import java.io.ByteArrayOutputStream; |
| 21 | import java.io.IOException; | 31 | import java.io.IOException; |
| @@ -25,205 +35,197 @@ import java.util.Map; | |||
| 25 | import java.util.jar.JarEntry; | 35 | import java.util.jar.JarEntry; |
| 26 | import java.util.jar.JarFile; | 36 | import java.util.jar.JarFile; |
| 27 | 37 | ||
| 28 | import cuchaz.enigma.analysis.BridgeMarker; | ||
| 29 | import cuchaz.enigma.analysis.JarIndex; | ||
| 30 | import cuchaz.enigma.bytecode.*; | ||
| 31 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 32 | import cuchaz.enigma.mapping.Translator; | ||
| 33 | import javassist.*; | ||
| 34 | import javassist.bytecode.Descriptor; | ||
| 35 | |||
| 36 | public class TranslatingTypeLoader implements ITypeLoader { | 38 | public class TranslatingTypeLoader implements ITypeLoader { |
| 37 | 39 | ||
| 38 | private JarFile jar; | 40 | private JarFile jar; |
| 39 | private JarIndex jarIndex; | 41 | private JarIndex jarIndex; |
| 40 | private Translator obfuscatingTranslator; | 42 | private Translator obfuscatingTranslator; |
| 41 | private Translator deobfuscatingTranslator; | 43 | private Translator deobfuscatingTranslator; |
| 42 | private Map<String, byte[]> cache; | 44 | private Map<String, byte[]> cache; |
| 43 | private ClasspathTypeLoader defaultTypeLoader; | 45 | private ClasspathTypeLoader defaultTypeLoader; |
| 44 | 46 | ||
| 45 | public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { | 47 | public TranslatingTypeLoader(JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { |
| 46 | this.jar = jar; | 48 | this.jar = jar; |
| 47 | this.jarIndex = jarIndex; | 49 | this.jarIndex = jarIndex; |
| 48 | this.obfuscatingTranslator = obfuscatingTranslator; | 50 | this.obfuscatingTranslator = obfuscatingTranslator; |
| 49 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 51 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 50 | this.cache = Maps.newHashMap(); | 52 | this.cache = Maps.newHashMap(); |
| 51 | this.defaultTypeLoader = new ClasspathTypeLoader(); | 53 | this.defaultTypeLoader = new ClasspathTypeLoader(); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | public void clearCache() { | 56 | public void clearCache() { |
| 55 | this.cache.clear(); | 57 | this.cache.clear(); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | @Override | 60 | @Override |
| 59 | public boolean tryLoadType(String className, Buffer out) { | 61 | public boolean tryLoadType(String className, Buffer out) { |
| 60 | 62 | ||
| 61 | // check the cache | 63 | // check the cache |
| 62 | byte[] data; | 64 | byte[] data; |
| 63 | if (this.cache.containsKey(className)) { | 65 | if (this.cache.containsKey(className)) { |
| 64 | data = this.cache.get(className); | 66 | data = this.cache.get(className); |
| 65 | } else { | 67 | } else { |
| 66 | data = loadType(className); | 68 | data = loadType(className); |
| 67 | this.cache.put(className, data); | 69 | this.cache.put(className, data); |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 70 | if (data == null) { | 72 | if (data == null) { |
| 71 | // chain to default type loader | 73 | // chain to default type loader |
| 72 | return this.defaultTypeLoader.tryLoadType(className, out); | 74 | return this.defaultTypeLoader.tryLoadType(className, out); |
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | // send the class to the decompiler | 77 | // send the class to the decompiler |
| 76 | out.reset(data.length); | 78 | out.reset(data.length); |
| 77 | System.arraycopy(data, 0, out.array(), out.position(), data.length); | 79 | System.arraycopy(data, 0, out.array(), out.position(), data.length); |
| 78 | out.position(0); | 80 | out.position(0); |
| 79 | return true; | 81 | return true; |
| 80 | } | 82 | } |
| 81 | 83 | ||
| 82 | public CtClass loadClass(String deobfClassName) { | 84 | public CtClass loadClass(String deobfClassName) { |
| 83 | 85 | ||
| 84 | byte[] data = loadType(deobfClassName); | 86 | byte[] data = loadType(deobfClassName); |
| 85 | if (data == null) { | 87 | if (data == null) { |
| 86 | return null; | 88 | return null; |
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | // return a javassist handle for the class | 91 | // return a javassist handle for the class |
| 90 | String javaClassFileName = Descriptor.toJavaName(deobfClassName); | 92 | String javaClassFileName = Descriptor.toJavaName(deobfClassName); |
| 91 | ClassPool classPool = new ClassPool(); | 93 | ClassPool classPool = new ClassPool(); |
| 92 | classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); | 94 | classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, data)); |
| 93 | try { | 95 | try { |
| 94 | return classPool.get(javaClassFileName); | 96 | return classPool.get(javaClassFileName); |
| 95 | } catch (NotFoundException ex) { | 97 | } catch (NotFoundException ex) { |
| 96 | throw new Error(ex); | 98 | throw new Error(ex); |
| 97 | } | 99 | } |
| 98 | } | 100 | } |
| 99 | 101 | ||
| 100 | private byte[] loadType(String className) { | 102 | private byte[] loadType(String className) { |
| 101 | 103 | ||
| 102 | // NOTE: don't know if class name is obf or deobf | 104 | // NOTE: don't know if class name is obf or deobf |
| 103 | ClassEntry classEntry = new ClassEntry(className); | 105 | ClassEntry classEntry = new ClassEntry(className); |
| 104 | ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry); | 106 | ClassEntry obfClassEntry = this.obfuscatingTranslator.translateEntry(classEntry); |
| 105 | 107 | ||
| 106 | // is this an inner class referenced directly? (ie trying to load b instead of a$b) | 108 | // is this an inner class referenced directly? (ie trying to load b instead of a$b) |
| 107 | if (!obfClassEntry.isInnerClass()) { | 109 | if (!obfClassEntry.isInnerClass()) { |
| 108 | List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry); | 110 | List<ClassEntry> classChain = this.jarIndex.getObfClassChain(obfClassEntry); |
| 109 | if (classChain.size() > 1) { | 111 | if (classChain.size() > 1) { |
| 110 | System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", | 112 | System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", |
| 111 | className, obfClassEntry.buildClassEntry(classChain) | 113 | className, obfClassEntry.buildClassEntry(classChain) |
| 112 | )); | 114 | )); |
| 113 | return null; | 115 | return null; |
| 114 | } | 116 | } |
| 115 | } | 117 | } |
| 116 | 118 | ||
| 117 | // is this a class we should even know about? | 119 | // is this a class we should even know about? |
| 118 | if (!this.jarIndex.containsObfClass(obfClassEntry)) { | 120 | if (!this.jarIndex.containsObfClass(obfClassEntry)) { |
| 119 | return null; | 121 | return null; |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | // DEBUG | 124 | // DEBUG |
| 123 | //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); | 125 | //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); |
| 124 | 126 | ||
| 125 | // find the class in the jar | 127 | // find the class in the jar |
| 126 | String classInJarName = findClassInJar(obfClassEntry); | 128 | String classInJarName = findClassInJar(obfClassEntry); |
| 127 | if (classInJarName == null) { | 129 | if (classInJarName == null) { |
| 128 | // couldn't find it | 130 | // couldn't find it |
| 129 | return null; | 131 | return null; |
| 130 | } | 132 | } |
| 131 | 133 | ||
| 132 | try { | 134 | try { |
| 133 | // read the class file into a buffer | 135 | // read the class file into a buffer |
| 134 | ByteArrayOutputStream data = new ByteArrayOutputStream(); | 136 | ByteArrayOutputStream data = new ByteArrayOutputStream(); |
| 135 | byte[] buf = new byte[1024 * 1024]; // 1 KiB | 137 | byte[] buf = new byte[1024 * 1024]; // 1 KiB |
| 136 | InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class")); | 138 | InputStream in = this.jar.getInputStream(this.jar.getJarEntry(classInJarName + ".class")); |
| 137 | while (true) { | 139 | while (true) { |
| 138 | int bytesRead = in.read(buf); | 140 | int bytesRead = in.read(buf); |
| 139 | if (bytesRead <= 0) { | 141 | if (bytesRead <= 0) { |
| 140 | break; | 142 | break; |
| 141 | } | 143 | } |
| 142 | data.write(buf, 0, bytesRead); | 144 | data.write(buf, 0, bytesRead); |
| 143 | } | 145 | } |
| 144 | data.close(); | 146 | data.close(); |
| 145 | in.close(); | 147 | in.close(); |
| 146 | buf = data.toByteArray(); | 148 | buf = data.toByteArray(); |
| 147 | 149 | ||
| 148 | // load the javassist handle to the raw class | 150 | // load the javassist handle to the raw class |
| 149 | ClassPool classPool = new ClassPool(); | 151 | ClassPool classPool = new ClassPool(); |
| 150 | String classInJarJavaName = Descriptor.toJavaName(classInJarName); | 152 | String classInJarJavaName = Descriptor.toJavaName(classInJarName); |
| 151 | classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf)); | 153 | classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf)); |
| 152 | CtClass c = classPool.get(classInJarJavaName); | 154 | CtClass c = classPool.get(classInJarJavaName); |
| 153 | 155 | ||
| 154 | c = transformClass(c); | 156 | c = transformClass(c); |
| 155 | 157 | ||
| 156 | // sanity checking | 158 | // sanity checking |
| 157 | assertClassName(c, classEntry); | 159 | assertClassName(c, classEntry); |
| 158 | 160 | ||
| 159 | // DEBUG | 161 | // DEBUG |
| 160 | //Util.writeClass( c ); | 162 | //Util.writeClass( c ); |
| 161 | 163 | ||
| 162 | // we have a transformed class! | 164 | // we have a transformed class! |
| 163 | return c.toBytecode(); | 165 | return c.toBytecode(); |
| 164 | } catch (IOException | NotFoundException | CannotCompileException ex) { | 166 | } catch (IOException | NotFoundException | CannotCompileException ex) { |
| 165 | throw new Error(ex); | 167 | throw new Error(ex); |
| 166 | } | 168 | } |
| 167 | } | 169 | } |
| 168 | 170 | ||
| 169 | private String findClassInJar(ClassEntry obfClassEntry) { | 171 | private String findClassInJar(ClassEntry obfClassEntry) { |
| 170 | 172 | ||
| 171 | // try to find the class in the jar | 173 | // try to find the class in the jar |
| 172 | for (String className : getClassNamesToTry(obfClassEntry)) { | 174 | for (String className : getClassNamesToTry(obfClassEntry)) { |
| 173 | JarEntry jarEntry = this.jar.getJarEntry(className + ".class"); | 175 | JarEntry jarEntry = this.jar.getJarEntry(className + ".class"); |
| 174 | if (jarEntry != null) { | 176 | if (jarEntry != null) { |
| 175 | return className; | 177 | return className; |
| 176 | } | 178 | } |
| 177 | } | 179 | } |
| 178 | 180 | ||
| 179 | // didn't find it ;_; | 181 | // didn't find it ;_; |
| 180 | return null; | 182 | return null; |
| 181 | } | 183 | } |
| 182 | 184 | ||
| 183 | public List<String> getClassNamesToTry(String className) { | 185 | public List<String> getClassNamesToTry(String className) { |
| 184 | return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className))); | 186 | return getClassNamesToTry(this.obfuscatingTranslator.translateEntry(new ClassEntry(className))); |
| 185 | } | 187 | } |
| 186 | 188 | ||
| 187 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | 189 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { |
| 188 | List<String> classNamesToTry = Lists.newArrayList(); | 190 | List<String> classNamesToTry = Lists.newArrayList(); |
| 189 | classNamesToTry.add(obfClassEntry.getName()); | 191 | classNamesToTry.add(obfClassEntry.getName()); |
| 190 | if (obfClassEntry.isInnerClass()) { | 192 | if (obfClassEntry.isInnerClass()) { |
| 191 | // try just the inner class name | 193 | // try just the inner class name |
| 192 | classNamesToTry.add(obfClassEntry.getInnermostClassName()); | 194 | classNamesToTry.add(obfClassEntry.getInnermostClassName()); |
| 193 | } | 195 | } |
| 194 | return classNamesToTry; | 196 | return classNamesToTry; |
| 195 | } | 197 | } |
| 196 | 198 | ||
| 197 | public CtClass transformClass(CtClass c) | 199 | public CtClass transformClass(CtClass c) |
| 198 | throws IOException, NotFoundException, CannotCompileException { | 200 | throws IOException, NotFoundException, CannotCompileException { |
| 199 | 201 | ||
| 200 | // reconstruct inner classes | 202 | // reconstruct inner classes |
| 201 | new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c); | 203 | new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c); |
| 202 | 204 | ||
| 203 | // re-get the javassist handle since we changed class names | 205 | // re-get the javassist handle since we changed class names |
| 204 | ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 206 | ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 205 | String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); | 207 | String javaClassReconstructedName = Descriptor.toJavaName(obfClassEntry.getName()); |
| 206 | ClassPool classPool = new ClassPool(); | 208 | ClassPool classPool = new ClassPool(); |
| 207 | classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); | 209 | classPool.insertClassPath(new ByteArrayClassPath(javaClassReconstructedName, c.toBytecode())); |
| 208 | c = classPool.get(javaClassReconstructedName); | 210 | c = classPool.get(javaClassReconstructedName); |
| 209 | 211 | ||
| 210 | // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) | 212 | // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) |
| 211 | assertClassName(c, obfClassEntry); | 213 | assertClassName(c, obfClassEntry); |
| 212 | 214 | ||
| 213 | // do all kinds of deobfuscating transformations on the class | 215 | // do all kinds of deobfuscating transformations on the class |
| 214 | new BridgeMarker(this.jarIndex).markBridges(c); | 216 | new BridgeMarker(this.jarIndex).markBridges(c); |
| 215 | new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c); | 217 | new MethodParameterWriter(this.deobfuscatingTranslator).writeMethodArguments(c); |
| 216 | new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c); | 218 | new LocalVariableRenamer(this.deobfuscatingTranslator).rename(c); |
| 217 | new ClassTranslator(this.deobfuscatingTranslator).translate(c); | 219 | new ClassTranslator(this.deobfuscatingTranslator).translate(c); |
| 218 | 220 | ||
| 219 | return c; | 221 | return c; |
| 220 | } | 222 | } |
| 221 | 223 | ||
| 222 | private void assertClassName(CtClass c, ClassEntry obfClassEntry) { | 224 | private void assertClassName(CtClass c, ClassEntry obfClassEntry) { |
| 223 | String name1 = Descriptor.toJvmName(c.getName()); | 225 | String name1 = Descriptor.toJvmName(c.getName()); |
| 224 | assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); | 226 | assert (name1.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name1); |
| 225 | 227 | ||
| 226 | String name2 = Descriptor.toJvmName(c.getClassFile().getName()); | 228 | String name2 = Descriptor.toJvmName(c.getClassFile().getName()); |
| 227 | assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); | 229 | assert (name2.equals(obfClassEntry.getName())) : String.format("Looking for %s, instead found %s", obfClassEntry.getName(), name2); |
| 228 | } | 230 | } |
| 229 | } | 231 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/Access.java b/src/main/java/cuchaz/enigma/analysis/Access.java index b8a7b2c8..547d85ef 100644 --- a/src/main/java/cuchaz/enigma/analysis/Access.java +++ b/src/main/java/cuchaz/enigma/analysis/Access.java | |||
| @@ -8,40 +8,41 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.analysis; | ||
| 12 | 11 | ||
| 13 | import java.lang.reflect.Modifier; | 12 | package cuchaz.enigma.analysis; |
| 14 | 13 | ||
| 15 | import javassist.CtBehavior; | 14 | import javassist.CtBehavior; |
| 16 | import javassist.CtField; | 15 | import javassist.CtField; |
| 17 | 16 | ||
| 17 | import java.lang.reflect.Modifier; | ||
| 18 | |||
| 18 | public enum Access { | 19 | public enum Access { |
| 19 | 20 | ||
| 20 | PUBLIC, PROTECTED, PACKAGE, PRIVATE; | 21 | PUBLIC, PROTECTED, PACKAGE, PRIVATE; |
| 21 | 22 | ||
| 22 | public static Access get(CtBehavior behavior) { | 23 | public static Access get(CtBehavior behavior) { |
| 23 | return get(behavior.getModifiers()); | 24 | return get(behavior.getModifiers()); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public static Access get(CtField field) { | 27 | public static Access get(CtField field) { |
| 27 | return get(field.getModifiers()); | 28 | return get(field.getModifiers()); |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | public static Access get(int modifiers) { | 31 | public static Access get(int modifiers) { |
| 31 | boolean isPublic = Modifier.isPublic(modifiers); | 32 | boolean isPublic = Modifier.isPublic(modifiers); |
| 32 | boolean isProtected = Modifier.isProtected(modifiers); | 33 | boolean isProtected = Modifier.isProtected(modifiers); |
| 33 | boolean isPrivate = Modifier.isPrivate(modifiers); | 34 | boolean isPrivate = Modifier.isPrivate(modifiers); |
| 34 | 35 | ||
| 35 | if (isPublic && !isProtected && !isPrivate) { | 36 | if (isPublic && !isProtected && !isPrivate) { |
| 36 | return PUBLIC; | 37 | return PUBLIC; |
| 37 | } else if (!isPublic && isProtected && !isPrivate) { | 38 | } else if (!isPublic && isProtected && !isPrivate) { |
| 38 | return PROTECTED; | 39 | return PROTECTED; |
| 39 | } else if (!isPublic && !isProtected && isPrivate) { | 40 | } else if (!isPublic && !isProtected && isPrivate) { |
| 40 | return PRIVATE; | 41 | return PRIVATE; |
| 41 | } else if (!isPublic && !isProtected && !isPrivate) { | 42 | } else if (!isPublic && !isProtected && !isPrivate) { |
| 42 | return PACKAGE; | 43 | return PACKAGE; |
| 43 | } | 44 | } |
| 44 | // assume public by default | 45 | // assume public by default |
| 45 | return PUBLIC; | 46 | return PUBLIC; |
| 46 | } | 47 | } |
| 47 | } | 48 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java index 52d5b311..6556b2cf 100644 --- a/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/BehaviorReferenceTreeNode.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| @@ -20,85 +21,73 @@ import javax.swing.tree.TreeNode; | |||
| 20 | import java.util.Set; | 21 | import java.util.Set; |
| 21 | 22 | ||
| 22 | public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode | 23 | public class BehaviorReferenceTreeNode extends DefaultMutableTreeNode |
| 23 | implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> | 24 | implements ReferenceTreeNode<BehaviorEntry, BehaviorEntry> { |
| 24 | { | ||
| 25 | 25 | ||
| 26 | private Translator deobfuscatingTranslator; | 26 | private Translator deobfuscatingTranslator; |
| 27 | private BehaviorEntry entry; | 27 | private BehaviorEntry entry; |
| 28 | private EntryReference<BehaviorEntry, BehaviorEntry> reference; | 28 | private EntryReference<BehaviorEntry, BehaviorEntry> reference; |
| 29 | private Access access; | 29 | private Access access; |
| 30 | 30 | ||
| 31 | public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) | 31 | public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, BehaviorEntry entry) { |
| 32 | { | 32 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 33 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 33 | this.entry = entry; |
| 34 | this.entry = entry; | 34 | this.reference = null; |
| 35 | this.reference = null; | 35 | } |
| 36 | } | ||
| 37 | 36 | ||
| 38 | public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, | 37 | public BehaviorReferenceTreeNode(Translator deobfuscatingTranslator, |
| 39 | EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) | 38 | EntryReference<BehaviorEntry, BehaviorEntry> reference, Access access) { |
| 40 | { | 39 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 41 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 40 | this.entry = reference.entry; |
| 42 | this.entry = reference.entry; | 41 | this.reference = reference; |
| 43 | this.reference = reference; | 42 | this.access = access; |
| 44 | this.access = access; | 43 | } |
| 45 | } | ||
| 46 | 44 | ||
| 47 | @Override public BehaviorEntry getEntry() | 45 | @Override |
| 48 | { | 46 | public BehaviorEntry getEntry() { |
| 49 | return this.entry; | 47 | return this.entry; |
| 50 | } | 48 | } |
| 51 | 49 | ||
| 52 | @Override public EntryReference<BehaviorEntry, BehaviorEntry> getReference() | 50 | @Override |
| 53 | { | 51 | public EntryReference<BehaviorEntry, BehaviorEntry> getReference() { |
| 54 | return this.reference; | 52 | return this.reference; |
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | @Override public String toString() | 55 | @Override |
| 58 | { | 56 | public String toString() { |
| 59 | if (this.reference != null) | 57 | if (this.reference != null) { |
| 60 | { | 58 | return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), |
| 61 | return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), | 59 | this.access); |
| 62 | this.access); | 60 | } |
| 63 | } | 61 | return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); |
| 64 | return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); | 62 | } |
| 65 | } | ||
| 66 | 63 | ||
| 67 | public void load(JarIndex index, boolean recurse) | 64 | public void load(JarIndex index, boolean recurse) { |
| 68 | { | 65 | // get all the child nodes |
| 69 | // get all the child nodes | 66 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) { |
| 70 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.entry)) | 67 | add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); |
| 71 | { | 68 | } |
| 72 | add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); | ||
| 73 | } | ||
| 74 | 69 | ||
| 75 | if (recurse && this.children != null) | 70 | if (recurse && this.children != null) { |
| 76 | { | 71 | for (Object child : this.children) { |
| 77 | for (Object child : this.children) | 72 | if (child instanceof BehaviorReferenceTreeNode) { |
| 78 | { | 73 | BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; |
| 79 | if (child instanceof BehaviorReferenceTreeNode) | ||
| 80 | { | ||
| 81 | BehaviorReferenceTreeNode node = (BehaviorReferenceTreeNode) child; | ||
| 82 | 74 | ||
| 83 | // don't recurse into ancestor | 75 | // don't recurse into ancestor |
| 84 | Set<Entry> ancestors = Sets.newHashSet(); | 76 | Set<Entry> ancestors = Sets.newHashSet(); |
| 85 | TreeNode n = node; | 77 | TreeNode n = node; |
| 86 | while (n.getParent() != null) | 78 | while (n.getParent() != null) { |
| 87 | { | 79 | n = n.getParent(); |
| 88 | n = n.getParent(); | 80 | if (n instanceof BehaviorReferenceTreeNode) { |
| 89 | if (n instanceof BehaviorReferenceTreeNode) | 81 | ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); |
| 90 | { | 82 | } |
| 91 | ancestors.add(((BehaviorReferenceTreeNode) n).getEntry()); | 83 | } |
| 92 | } | 84 | if (ancestors.contains(node.getEntry())) { |
| 93 | } | 85 | continue; |
| 94 | if (ancestors.contains(node.getEntry())) | 86 | } |
| 95 | { | ||
| 96 | continue; | ||
| 97 | } | ||
| 98 | 87 | ||
| 99 | node.load(index, true); | 88 | node.load(index, true); |
| 100 | } | 89 | } |
| 101 | } | 90 | } |
| 102 | } | 91 | } |
| 103 | } | 92 | } |
| 104 | } | 93 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java index 0f4be7d9..81e750c1 100644 --- a/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java +++ b/src/main/java/cuchaz/enigma/analysis/BridgeMarker.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.EntryFactory; | 14 | import cuchaz.enigma.mapping.EntryFactory; |
| @@ -18,26 +19,26 @@ import javassist.bytecode.AccessFlag; | |||
| 18 | 19 | ||
| 19 | public class BridgeMarker { | 20 | public class BridgeMarker { |
| 20 | 21 | ||
| 21 | private JarIndex jarIndex; | 22 | private JarIndex jarIndex; |
| 22 | 23 | ||
| 23 | public BridgeMarker(JarIndex jarIndex) { | 24 | public BridgeMarker(JarIndex jarIndex) { |
| 24 | this.jarIndex = jarIndex; | 25 | this.jarIndex = jarIndex; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | public void markBridges(CtClass c) { | 28 | public void markBridges(CtClass c) { |
| 28 | 29 | ||
| 29 | for (CtMethod method : c.getDeclaredMethods()) { | 30 | for (CtMethod method : c.getDeclaredMethods()) { |
| 30 | MethodEntry methodEntry = EntryFactory.getMethodEntry(method); | 31 | MethodEntry methodEntry = EntryFactory.getMethodEntry(method); |
| 31 | 32 | ||
| 32 | // is this a bridge method? | 33 | // is this a bridge method? |
| 33 | MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); | 34 | MethodEntry bridgedMethodEntry = this.jarIndex.getBridgedMethod(methodEntry); |
| 34 | if (bridgedMethodEntry != null) { | 35 | if (bridgedMethodEntry != null) { |
| 35 | 36 | ||
| 36 | // it's a bridge method! add the bridge flag | 37 | // it's a bridge method! add the bridge flag |
| 37 | int flags = method.getMethodInfo().getAccessFlags(); | 38 | int flags = method.getMethodInfo().getAccessFlags(); |
| 38 | flags |= AccessFlag.BRIDGE; | 39 | flags |= AccessFlag.BRIDGE; |
| 39 | method.getMethodInfo().setAccessFlags(flags); | 40 | method.getMethodInfo().setAccessFlags(flags); |
| 40 | } | 41 | } |
| 41 | } | 42 | } |
| 42 | } | 43 | } |
| 43 | } | 44 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 70ece243..f2fb2f8d 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java | |||
| @@ -8,69 +8,68 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | |||
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 18 | |||
| 19 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 20 | import cuchaz.enigma.mapping.MethodEntry; | 16 | import cuchaz.enigma.mapping.MethodEntry; |
| 21 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.mapping.Translator; |
| 22 | 18 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 23 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { | 22 | public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { |
| 24 | 23 | ||
| 25 | private Translator deobfuscatingTranslator; | 24 | private Translator deobfuscatingTranslator; |
| 26 | private ClassEntry entry; | 25 | private ClassEntry entry; |
| 27 | 26 | ||
| 28 | public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { | 27 | public ClassImplementationsTreeNode(Translator deobfuscatingTranslator, ClassEntry entry) { |
| 29 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 28 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 30 | this.entry = entry; | 29 | this.entry = entry; |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | public ClassEntry getClassEntry() { | 32 | public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { |
| 34 | return this.entry; | 33 | // is this the node? |
| 35 | } | 34 | if (node.entry.equals(entry.getClassEntry())) { |
| 35 | return node; | ||
| 36 | } | ||
| 36 | 37 | ||
| 37 | public String getDeobfClassName() { | 38 | // recurse |
| 38 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); | 39 | for (int i = 0; i < node.getChildCount(); i++) { |
| 39 | } | 40 | ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); |
| 41 | if (foundNode != null) { | ||
| 42 | return foundNode; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | return null; | ||
| 46 | } | ||
| 40 | 47 | ||
| 41 | @Override | 48 | public ClassEntry getClassEntry() { |
| 42 | public String toString() { | 49 | return this.entry; |
| 43 | String className = getDeobfClassName(); | 50 | } |
| 44 | if (className == null) { | ||
| 45 | className = this.entry.getClassName(); | ||
| 46 | } | ||
| 47 | return className; | ||
| 48 | } | ||
| 49 | 51 | ||
| 50 | public void load(JarIndex index) { | 52 | public String getDeobfClassName() { |
| 51 | // get all method implementations | 53 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); |
| 52 | List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); | 54 | } |
| 53 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { | ||
| 54 | nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); | ||
| 55 | } | ||
| 56 | 55 | ||
| 57 | // add them to this node | 56 | @Override |
| 58 | nodes.forEach(this::add); | 57 | public String toString() { |
| 59 | } | 58 | String className = getDeobfClassName(); |
| 59 | if (className == null) { | ||
| 60 | className = this.entry.getClassName(); | ||
| 61 | } | ||
| 62 | return className; | ||
| 63 | } | ||
| 60 | 64 | ||
| 61 | public static ClassImplementationsTreeNode findNode(ClassImplementationsTreeNode node, MethodEntry entry) { | 65 | public void load(JarIndex index) { |
| 62 | // is this the node? | 66 | // get all method implementations |
| 63 | if (node.entry.equals(entry.getClassEntry())) { | 67 | List<ClassImplementationsTreeNode> nodes = Lists.newArrayList(); |
| 64 | return node; | 68 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { |
| 65 | } | 69 | nodes.add(new ClassImplementationsTreeNode(this.deobfuscatingTranslator, new ClassEntry(implementingClassName))); |
| 70 | } | ||
| 66 | 71 | ||
| 67 | // recurse | 72 | // add them to this node |
| 68 | for (int i = 0; i < node.getChildCount(); i++) { | 73 | nodes.forEach(this::add); |
| 69 | ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); | 74 | } |
| 70 | if (foundNode != null) { | ||
| 71 | return foundNode; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | return null; | ||
| 75 | } | ||
| 76 | } | 75 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 8a60fc72..24e7cb0b 100644 --- a/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java | |||
| @@ -8,74 +8,73 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | |||
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 18 | |||
| 19 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 20 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.mapping.Translator; |
| 21 | 17 | ||
| 18 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 19 | import java.util.List; | ||
| 20 | |||
| 22 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { | 21 | public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { |
| 23 | 22 | ||
| 24 | private Translator deobfuscatingTranslator; | 23 | private Translator deobfuscatingTranslator; |
| 25 | private String obfClassName; | 24 | private String obfClassName; |
| 26 | 25 | ||
| 27 | public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { | 26 | public ClassInheritanceTreeNode(Translator deobfuscatingTranslator, String obfClassName) { |
| 28 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 27 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 29 | this.obfClassName = obfClassName; | 28 | this.obfClassName = obfClassName; |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | public String getObfClassName() { | 31 | public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { |
| 33 | return this.obfClassName; | 32 | // is this the node? |
| 34 | } | 33 | if (node.getObfClassName().equals(entry.getName())) { |
| 34 | return node; | ||
| 35 | } | ||
| 35 | 36 | ||
| 36 | public String getDeobfClassName() { | 37 | // recurse |
| 37 | return this.deobfuscatingTranslator.translateClass(this.obfClassName); | 38 | for (int i = 0; i < node.getChildCount(); i++) { |
| 38 | } | 39 | ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); |
| 40 | if (foundNode != null) { | ||
| 41 | return foundNode; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | return null; | ||
| 45 | } | ||
| 39 | 46 | ||
| 40 | @Override | 47 | public String getObfClassName() { |
| 41 | public String toString() { | 48 | return this.obfClassName; |
| 42 | String deobfClassName = getDeobfClassName(); | 49 | } |
| 43 | if (deobfClassName != null) { | ||
| 44 | return deobfClassName; | ||
| 45 | } | ||
| 46 | return this.obfClassName; | ||
| 47 | } | ||
| 48 | 50 | ||
| 49 | public void load(TranslationIndex ancestries, boolean recurse) { | 51 | public String getDeobfClassName() { |
| 50 | // get all the child nodes | 52 | return this.deobfuscatingTranslator.translateClass(this.obfClassName); |
| 51 | List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); | 53 | } |
| 52 | for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { | ||
| 53 | nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); | ||
| 54 | } | ||
| 55 | 54 | ||
| 56 | // add them to this node | 55 | @Override |
| 57 | nodes.forEach(this::add); | 56 | public String toString() { |
| 57 | String deobfClassName = getDeobfClassName(); | ||
| 58 | if (deobfClassName != null) { | ||
| 59 | return deobfClassName; | ||
| 60 | } | ||
| 61 | return this.obfClassName; | ||
| 62 | } | ||
| 58 | 63 | ||
| 59 | if (recurse) { | 64 | public void load(TranslationIndex ancestries, boolean recurse) { |
| 60 | for (ClassInheritanceTreeNode node : nodes) { | 65 | // get all the child nodes |
| 61 | node.load(ancestries, true); | 66 | List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); |
| 62 | } | 67 | for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(this.obfClassName))) { |
| 63 | } | 68 | nodes.add(new ClassInheritanceTreeNode(this.deobfuscatingTranslator, subclassEntry.getName())); |
| 64 | } | 69 | } |
| 65 | 70 | ||
| 66 | public static ClassInheritanceTreeNode findNode(ClassInheritanceTreeNode node, ClassEntry entry) { | 71 | // add them to this node |
| 67 | // is this the node? | 72 | nodes.forEach(this::add); |
| 68 | if (node.getObfClassName().equals(entry.getName())) { | ||
| 69 | return node; | ||
| 70 | } | ||
| 71 | 73 | ||
| 72 | // recurse | 74 | if (recurse) { |
| 73 | for (int i = 0; i < node.getChildCount(); i++) { | 75 | for (ClassInheritanceTreeNode node : nodes) { |
| 74 | ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); | 76 | node.load(ancestries, true); |
| 75 | if (foundNode != null) { | 77 | } |
| 76 | return foundNode; | 78 | } |
| 77 | } | 79 | } |
| 78 | } | ||
| 79 | return null; | ||
| 80 | } | ||
| 81 | } | 80 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/src/main/java/cuchaz/enigma/analysis/EntryReference.java index ad4baf81..3761fca8 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryReference.java | |||
| @@ -8,116 +8,117 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.analysis; | ||
| 12 | 11 | ||
| 13 | import java.util.Arrays; | 12 | package cuchaz.enigma.analysis; |
| 14 | import java.util.List; | ||
| 15 | 13 | ||
| 16 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 17 | import cuchaz.enigma.mapping.ConstructorEntry; | 15 | import cuchaz.enigma.mapping.ConstructorEntry; |
| 18 | import cuchaz.enigma.mapping.Entry; | 16 | import cuchaz.enigma.mapping.Entry; |
| 19 | import cuchaz.enigma.utils.Utils; | 17 | import cuchaz.enigma.utils.Utils; |
| 20 | 18 | ||
| 19 | import java.util.Arrays; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 21 | public class EntryReference<E extends Entry, C extends Entry> { | 22 | public class EntryReference<E extends Entry, C extends Entry> { |
| 22 | 23 | ||
| 23 | private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); | 24 | private static final List<String> ConstructorNonNames = Arrays.asList("this", "super", "static"); |
| 24 | public E entry; | 25 | public E entry; |
| 25 | public C context; | 26 | public C context; |
| 26 | 27 | ||
| 27 | private boolean sourceName; | 28 | private boolean sourceName; |
| 28 | 29 | ||
| 29 | public EntryReference(E entry, String sourceName) { | 30 | public EntryReference(E entry, String sourceName) { |
| 30 | this(entry, sourceName, null); | 31 | this(entry, sourceName, null); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | public EntryReference(E entry, String sourceName, C context) { | 34 | public EntryReference(E entry, String sourceName, C context) { |
| 34 | if (entry == null) { | 35 | if (entry == null) { |
| 35 | throw new IllegalArgumentException("Entry cannot be null!"); | 36 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | this.entry = entry; | 39 | this.entry = entry; |
| 39 | this.context = context; | 40 | this.context = context; |
| 40 | 41 | ||
| 41 | this.sourceName = sourceName != null && sourceName.length() > 0; | 42 | this.sourceName = sourceName != null && !sourceName.isEmpty(); |
| 42 | if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { | 43 | if (entry instanceof ConstructorEntry && ConstructorNonNames.contains(sourceName)) { |
| 43 | this.sourceName = false; | 44 | this.sourceName = false; |
| 44 | } | 45 | } |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | public EntryReference(E entry, C context, EntryReference<E, C> other) { | 48 | public EntryReference(E entry, C context, EntryReference<E, C> other) { |
| 48 | this.entry = entry; | 49 | this.entry = entry; |
| 49 | this.context = context; | 50 | this.context = context; |
| 50 | this.sourceName = other.sourceName; | 51 | this.sourceName = other.sourceName; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | public ClassEntry getLocationClassEntry() { | 54 | public ClassEntry getLocationClassEntry() { |
| 54 | if (context != null) { | 55 | if (context != null) { |
| 55 | return context.getClassEntry(); | 56 | return context.getClassEntry(); |
| 56 | } | 57 | } |
| 57 | return entry.getClassEntry(); | 58 | return entry.getClassEntry(); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | public boolean isNamed() { | 61 | public boolean isNamed() { |
| 61 | return this.sourceName; | 62 | return this.sourceName; |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | public Entry getNameableEntry() { | 65 | public Entry getNameableEntry() { |
| 65 | if (entry instanceof ConstructorEntry) { | 66 | if (entry instanceof ConstructorEntry) { |
| 66 | // renaming a constructor really means renaming the class | 67 | // renaming a constructor really means renaming the class |
| 67 | return entry.getClassEntry(); | 68 | return entry.getClassEntry(); |
| 68 | } | 69 | } |
| 69 | return entry; | 70 | return entry; |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | public String getNamableName() { | 73 | public String getNamableName() { |
| 73 | if (getNameableEntry() instanceof ClassEntry) { | 74 | if (getNameableEntry() instanceof ClassEntry) { |
| 74 | ClassEntry classEntry = (ClassEntry) getNameableEntry(); | 75 | ClassEntry classEntry = (ClassEntry) getNameableEntry(); |
| 75 | if (classEntry.isInnerClass()) { | 76 | if (classEntry.isInnerClass()) { |
| 76 | // make sure we only rename the inner class name | 77 | // make sure we only rename the inner class name |
| 77 | return classEntry.getInnermostClassName(); | 78 | return classEntry.getInnermostClassName(); |
| 78 | } | 79 | } |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | return getNameableEntry().getName(); | 82 | return getNameableEntry().getName(); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | @Override | 85 | @Override |
| 85 | public int hashCode() { | 86 | public int hashCode() { |
| 86 | if (context != null) { | 87 | if (context != null) { |
| 87 | return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); | 88 | return Utils.combineHashesOrdered(entry.hashCode(), context.hashCode()); |
| 88 | } | 89 | } |
| 89 | return entry.hashCode(); | 90 | return entry.hashCode(); |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | @Override | 93 | @Override |
| 93 | public boolean equals(Object other) { | 94 | public boolean equals(Object other) { |
| 94 | return other instanceof EntryReference && equals((EntryReference<?, ?>) other); | 95 | return other instanceof EntryReference && equals((EntryReference<?, ?>) other); |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | public boolean equals(EntryReference<?, ?> other) { | 98 | public boolean equals(EntryReference<?, ?> other) { |
| 98 | // check entry first | 99 | // check entry first |
| 99 | boolean isEntrySame = entry.equals(other.entry); | 100 | boolean isEntrySame = entry.equals(other.entry); |
| 100 | if (!isEntrySame) { | 101 | if (!isEntrySame) { |
| 101 | return false; | 102 | return false; |
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | // check caller | 105 | // check caller |
| 105 | if (context == null && other.context == null) { | 106 | if (context == null && other.context == null) { |
| 106 | return true; | 107 | return true; |
| 107 | } else if (context != null && other.context != null) { | 108 | } else if (context != null && other.context != null) { |
| 108 | return context.equals(other.context); | 109 | return context.equals(other.context); |
| 109 | } | 110 | } |
| 110 | return false; | 111 | return false; |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | @Override | 114 | @Override |
| 114 | public String toString() { | 115 | public String toString() { |
| 115 | StringBuilder buf = new StringBuilder(); | 116 | StringBuilder buf = new StringBuilder(); |
| 116 | buf.append(entry); | 117 | buf.append(entry); |
| 117 | if (context != null) { | 118 | if (context != null) { |
| 118 | buf.append(" called from "); | 119 | buf.append(" called from "); |
| 119 | buf.append(context); | 120 | buf.append(context); |
| 120 | } | 121 | } |
| 121 | return buf.toString(); | 122 | return buf.toString(); |
| 122 | } | 123 | } |
| 123 | } | 124 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java index 7233fcf9..75806c33 100644 --- a/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java +++ b/src/main/java/cuchaz/enigma/analysis/EntryRenamer.java | |||
| @@ -8,140 +8,140 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 15 | import com.google.common.collect.Sets; | 16 | import com.google.common.collect.Sets; |
| 17 | import cuchaz.enigma.mapping.*; | ||
| 16 | 18 | ||
| 17 | import java.util.AbstractMap; | 19 | import java.util.AbstractMap; |
| 18 | import java.util.List; | 20 | import java.util.List; |
| 19 | import java.util.Map; | 21 | import java.util.Map; |
| 20 | import java.util.Set; | 22 | import java.util.Set; |
| 21 | 23 | ||
| 22 | import cuchaz.enigma.mapping.*; | ||
| 23 | |||
| 24 | public class EntryRenamer { | 24 | public class EntryRenamer { |
| 25 | 25 | ||
| 26 | public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) { | 26 | public static <T> void renameClassesInSet(Map<String, String> renames, Set<T> set) { |
| 27 | List<T> entries = Lists.newArrayList(); | 27 | List<T> entries = Lists.newArrayList(); |
| 28 | for (T val : set) { | 28 | for (T val : set) { |
| 29 | entries.add(renameClassesInThing(renames, val)); | 29 | entries.add(renameClassesInThing(renames, val)); |
| 30 | } | 30 | } |
| 31 | set.clear(); | 31 | set.clear(); |
| 32 | set.addAll(entries); | 32 | set.addAll(entries); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) { | 35 | public static <Key, Val> void renameClassesInMap(Map<String, String> renames, Map<Key, Val> map) { |
| 36 | // for each key/value pair... | 36 | // for each key/value pair... |
| 37 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | 37 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); |
| 38 | for (Map.Entry<Key, Val> entry : map.entrySet()) { | 38 | for (Map.Entry<Key, Val> entry : map.entrySet()) { |
| 39 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); | 39 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); |
| 40 | } | 40 | } |
| 41 | map.clear(); | 41 | map.clear(); |
| 42 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | 42 | for (Map.Entry<Key, Val> entry : entriesToAdd) { |
| 43 | map.put(entry.getKey(), entry.getValue()); | 43 | map.put(entry.getKey(), entry.getValue()); |
| 44 | } | 44 | } |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) { | 47 | public static <Key, Val> void renameClassesInMultimap(Map<String, String> renames, Multimap<Key, Val> map) { |
| 48 | // for each key/value pair... | 48 | // for each key/value pair... |
| 49 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | 49 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); |
| 50 | for (Map.Entry<Key, Val> entry : map.entries()) { | 50 | for (Map.Entry<Key, Val> entry : map.entries()) { |
| 51 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); | 51 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameClassesInThing(renames, entry.getKey()), renameClassesInThing(renames, entry.getValue()))); |
| 52 | } | 52 | } |
| 53 | map.clear(); | 53 | map.clear(); |
| 54 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | 54 | for (Map.Entry<Key, Val> entry : entriesToAdd) { |
| 55 | map.put(entry.getKey(), entry.getValue()); | 55 | map.put(entry.getKey(), entry.getValue()); |
| 56 | } | 56 | } |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) { | 59 | public static <Key, Val> void renameMethodsInMultimap(Map<MethodEntry, MethodEntry> renames, Multimap<Key, Val> map) { |
| 60 | // for each key/value pair... | 60 | // for each key/value pair... |
| 61 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | 61 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); |
| 62 | for (Map.Entry<Key, Val> entry : map.entries()) { | 62 | for (Map.Entry<Key, Val> entry : map.entries()) { |
| 63 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); | 63 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); |
| 64 | } | 64 | } |
| 65 | map.clear(); | 65 | map.clear(); |
| 66 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | 66 | for (Map.Entry<Key, Val> entry : entriesToAdd) { |
| 67 | map.put(entry.getKey(), entry.getValue()); | 67 | map.put(entry.getKey(), entry.getValue()); |
| 68 | } | 68 | } |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) { | 71 | public static <Key, Val> void renameMethodsInMap(Map<MethodEntry, MethodEntry> renames, Map<Key, Val> map) { |
| 72 | // for each key/value pair... | 72 | // for each key/value pair... |
| 73 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); | 73 | Set<Map.Entry<Key, Val>> entriesToAdd = Sets.newHashSet(); |
| 74 | for (Map.Entry<Key, Val> entry : map.entrySet()) { | 74 | for (Map.Entry<Key, Val> entry : map.entrySet()) { |
| 75 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); | 75 | entriesToAdd.add(new AbstractMap.SimpleEntry<>(renameMethodsInThing(renames, entry.getKey()), renameMethodsInThing(renames, entry.getValue()))); |
| 76 | } | 76 | } |
| 77 | map.clear(); | 77 | map.clear(); |
| 78 | for (Map.Entry<Key, Val> entry : entriesToAdd) { | 78 | for (Map.Entry<Key, Val> entry : entriesToAdd) { |
| 79 | map.put(entry.getKey(), entry.getValue()); | 79 | map.put(entry.getKey(), entry.getValue()); |
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | @SuppressWarnings("unchecked") | 83 | @SuppressWarnings("unchecked") |
| 84 | public static <T> T renameMethodsInThing(Map<MethodEntry,MethodEntry> renames, T thing) { | 84 | public static <T> T renameMethodsInThing(Map<MethodEntry, MethodEntry> renames, T thing) { |
| 85 | if (thing instanceof MethodEntry) { | 85 | if (thing instanceof MethodEntry) { |
| 86 | MethodEntry methodEntry = (MethodEntry)thing; | 86 | MethodEntry methodEntry = (MethodEntry) thing; |
| 87 | MethodEntry newMethodEntry = renames.get(methodEntry); | 87 | MethodEntry newMethodEntry = renames.get(methodEntry); |
| 88 | if (newMethodEntry != null) { | 88 | if (newMethodEntry != null) { |
| 89 | return (T)new MethodEntry( | 89 | return (T) new MethodEntry( |
| 90 | methodEntry.getClassEntry(), | 90 | methodEntry.getClassEntry(), |
| 91 | newMethodEntry.getName(), | 91 | newMethodEntry.getName(), |
| 92 | methodEntry.getSignature() | 92 | methodEntry.getSignature() |
| 93 | ); | 93 | ); |
| 94 | } | 94 | } |
| 95 | return thing; | 95 | return thing; |
| 96 | } else if (thing instanceof ArgumentEntry) { | 96 | } else if (thing instanceof ArgumentEntry) { |
| 97 | ArgumentEntry argumentEntry = (ArgumentEntry)thing; | 97 | ArgumentEntry argumentEntry = (ArgumentEntry) thing; |
| 98 | return (T)new ArgumentEntry( | 98 | return (T) new ArgumentEntry( |
| 99 | renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), | 99 | renameMethodsInThing(renames, argumentEntry.getBehaviorEntry()), |
| 100 | argumentEntry.getIndex(), | 100 | argumentEntry.getIndex(), |
| 101 | argumentEntry.getName() | 101 | argumentEntry.getName() |
| 102 | ); | 102 | ); |
| 103 | } else if (thing instanceof EntryReference) { | 103 | } else if (thing instanceof EntryReference) { |
| 104 | EntryReference<Entry,Entry> reference = (EntryReference<Entry,Entry>)thing; | 104 | EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; |
| 105 | reference.entry = renameMethodsInThing(renames, reference.entry); | 105 | reference.entry = renameMethodsInThing(renames, reference.entry); |
| 106 | reference.context = renameMethodsInThing(renames, reference.context); | 106 | reference.context = renameMethodsInThing(renames, reference.context); |
| 107 | return thing; | 107 | return thing; |
| 108 | } | 108 | } |
| 109 | return thing; | 109 | return thing; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | @SuppressWarnings("unchecked") | 112 | @SuppressWarnings("unchecked") |
| 113 | public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { | 113 | public static <T> T renameClassesInThing(final Map<String, String> renames, T thing) { |
| 114 | if (thing instanceof String) { | 114 | if (thing instanceof String) { |
| 115 | String stringEntry = (String) thing; | 115 | String stringEntry = (String) thing; |
| 116 | if (renames.containsKey(stringEntry)) { | 116 | if (renames.containsKey(stringEntry)) { |
| 117 | return (T) renames.get(stringEntry); | 117 | return (T) renames.get(stringEntry); |
| 118 | } | 118 | } |
| 119 | } else if (thing instanceof ClassEntry) { | 119 | } else if (thing instanceof ClassEntry) { |
| 120 | ClassEntry classEntry = (ClassEntry) thing; | 120 | ClassEntry classEntry = (ClassEntry) thing; |
| 121 | return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); | 121 | return (T) new ClassEntry(renameClassesInThing(renames, classEntry.getClassName())); |
| 122 | } else if (thing instanceof FieldEntry) { | 122 | } else if (thing instanceof FieldEntry) { |
| 123 | FieldEntry fieldEntry = (FieldEntry) thing; | 123 | FieldEntry fieldEntry = (FieldEntry) thing; |
| 124 | return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); | 124 | return (T) new FieldEntry(renameClassesInThing(renames, fieldEntry.getClassEntry()), fieldEntry.getName(), renameClassesInThing(renames, fieldEntry.getType())); |
| 125 | } else if (thing instanceof ConstructorEntry) { | 125 | } else if (thing instanceof ConstructorEntry) { |
| 126 | ConstructorEntry constructorEntry = (ConstructorEntry) thing; | 126 | ConstructorEntry constructorEntry = (ConstructorEntry) thing; |
| 127 | return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); | 127 | return (T) new ConstructorEntry(renameClassesInThing(renames, constructorEntry.getClassEntry()), renameClassesInThing(renames, constructorEntry.getSignature())); |
| 128 | } else if (thing instanceof MethodEntry) { | 128 | } else if (thing instanceof MethodEntry) { |
| 129 | MethodEntry methodEntry = (MethodEntry) thing; | 129 | MethodEntry methodEntry = (MethodEntry) thing; |
| 130 | return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); | 130 | return (T) new MethodEntry(renameClassesInThing(renames, methodEntry.getClassEntry()), methodEntry.getName(), renameClassesInThing(renames, methodEntry.getSignature())); |
| 131 | } else if (thing instanceof ArgumentEntry) { | 131 | } else if (thing instanceof ArgumentEntry) { |
| 132 | ArgumentEntry argumentEntry = (ArgumentEntry) thing; | 132 | ArgumentEntry argumentEntry = (ArgumentEntry) thing; |
| 133 | return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); | 133 | return (T) new ArgumentEntry(renameClassesInThing(renames, argumentEntry.getBehaviorEntry()), argumentEntry.getIndex(), argumentEntry.getName()); |
| 134 | } else if (thing instanceof EntryReference) { | 134 | } else if (thing instanceof EntryReference) { |
| 135 | EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; | 135 | EntryReference<Entry, Entry> reference = (EntryReference<Entry, Entry>) thing; |
| 136 | reference.entry = renameClassesInThing(renames, reference.entry); | 136 | reference.entry = renameClassesInThing(renames, reference.entry); |
| 137 | reference.context = renameClassesInThing(renames, reference.context); | 137 | reference.context = renameClassesInThing(renames, reference.context); |
| 138 | return thing; | 138 | return thing; |
| 139 | } else if (thing instanceof Signature) { | 139 | } else if (thing instanceof Signature) { |
| 140 | return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); | 140 | return (T) new Signature((Signature) thing, className -> renameClassesInThing(renames, className)); |
| 141 | } else if (thing instanceof Type) { | 141 | } else if (thing instanceof Type) { |
| 142 | return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); | 142 | return (T) new Type((Type) thing, className -> renameClassesInThing(renames, className)); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | return thing; | 145 | return thing; |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index 70cd0590..34d2eff1 100644 --- a/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java | |||
| @@ -8,72 +8,73 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.analysis; | ||
| 12 | 11 | ||
| 13 | import javax.swing.tree.DefaultMutableTreeNode; | 12 | package cuchaz.enigma.analysis; |
| 14 | 13 | ||
| 15 | import cuchaz.enigma.mapping.BehaviorEntry; | 14 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 16 | import cuchaz.enigma.mapping.FieldEntry; | 15 | import cuchaz.enigma.mapping.FieldEntry; |
| 17 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.mapping.Translator; |
| 18 | 17 | ||
| 18 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 19 | |||
| 19 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { | 20 | public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode<FieldEntry, BehaviorEntry> { |
| 20 | 21 | ||
| 21 | private Translator deobfuscatingTranslator; | 22 | private Translator deobfuscatingTranslator; |
| 22 | private FieldEntry entry; | 23 | private FieldEntry entry; |
| 23 | private EntryReference<FieldEntry, BehaviorEntry> reference; | 24 | private EntryReference<FieldEntry, BehaviorEntry> reference; |
| 24 | private Access access; | 25 | private Access access; |
| 25 | 26 | ||
| 26 | public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { | 27 | public FieldReferenceTreeNode(Translator deobfuscatingTranslator, FieldEntry entry) { |
| 27 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 28 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 28 | this.entry = entry; | 29 | this.entry = entry; |
| 29 | this.reference = null; | 30 | this.reference = null; |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { | 33 | private FieldReferenceTreeNode(Translator deobfuscatingTranslator, EntryReference<FieldEntry, BehaviorEntry> reference, Access access) { |
| 33 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 34 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 34 | this.entry = reference.entry; | 35 | this.entry = reference.entry; |
| 35 | this.reference = reference; | 36 | this.reference = reference; |
| 36 | this.access = access; | 37 | this.access = access; |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | @Override | 40 | @Override |
| 40 | public FieldEntry getEntry() { | 41 | public FieldEntry getEntry() { |
| 41 | return this.entry; | 42 | return this.entry; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | @Override | 45 | @Override |
| 45 | public EntryReference<FieldEntry, BehaviorEntry> getReference() { | 46 | public EntryReference<FieldEntry, BehaviorEntry> getReference() { |
| 46 | return this.reference; | 47 | return this.reference; |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | @Override | 50 | @Override |
| 50 | public String toString() { | 51 | public String toString() { |
| 51 | if (this.reference != null) { | 52 | if (this.reference != null) { |
| 52 | return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); | 53 | return String.format("%s (%s)", this.deobfuscatingTranslator.translateEntry(this.reference.context), this.access); |
| 53 | } | 54 | } |
| 54 | return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); | 55 | return this.deobfuscatingTranslator.translateEntry(this.entry).toString(); |
| 55 | } | 56 | } |
| 56 | 57 | ||
| 57 | public void load(JarIndex index, boolean recurse) { | 58 | public void load(JarIndex index, boolean recurse) { |
| 58 | // get all the child nodes | 59 | // get all the child nodes |
| 59 | if (this.reference == null) { | 60 | if (this.reference == null) { |
| 60 | for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { | 61 | for (EntryReference<FieldEntry, BehaviorEntry> reference : index.getFieldReferences(this.entry)) { |
| 61 | add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); | 62 | add(new FieldReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.entry))); |
| 62 | } | 63 | } |
| 63 | } else { | 64 | } else { |
| 64 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { | 65 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : index.getBehaviorReferences(this.reference.context)) { |
| 65 | add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); | 66 | add(new BehaviorReferenceTreeNode(this.deobfuscatingTranslator, reference, index.getAccess(this.reference.context))); |
| 66 | } | 67 | } |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | if (recurse && children != null) { | 70 | if (recurse && children != null) { |
| 70 | for (Object node : children) { | 71 | for (Object node : children) { |
| 71 | if (node instanceof BehaviorReferenceTreeNode) { | 72 | if (node instanceof BehaviorReferenceTreeNode) { |
| 72 | ((BehaviorReferenceTreeNode) node).load(index, true); | 73 | ((BehaviorReferenceTreeNode) node).load(index, true); |
| 73 | } else if (node instanceof FieldReferenceTreeNode) { | 74 | } else if (node instanceof FieldReferenceTreeNode) { |
| 74 | ((FieldReferenceTreeNode) node).load(index, true); | 75 | ((FieldReferenceTreeNode) node).load(index, true); |
| 75 | } | 76 | } |
| 76 | } | 77 | } |
| 77 | } | 78 | } |
| 78 | } | 79 | } |
| 79 | } | 80 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java index 0d18105e..87d3797d 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/main/java/cuchaz/enigma/analysis/JarClassIterator.java | |||
| @@ -8,9 +8,17 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.Constants; | ||
| 16 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 17 | import javassist.ByteArrayClassPath; | ||
| 18 | import javassist.ClassPool; | ||
| 19 | import javassist.CtClass; | ||
| 20 | import javassist.NotFoundException; | ||
| 21 | import javassist.bytecode.Descriptor; | ||
| 14 | 22 | ||
| 15 | import java.io.ByteArrayOutputStream; | 23 | import java.io.ByteArrayOutputStream; |
| 16 | import java.io.IOException; | 24 | import java.io.IOException; |
| @@ -21,103 +29,95 @@ import java.util.List; | |||
| 21 | import java.util.jar.JarEntry; | 29 | import java.util.jar.JarEntry; |
| 22 | import java.util.jar.JarFile; | 30 | import java.util.jar.JarFile; |
| 23 | 31 | ||
| 24 | import cuchaz.enigma.Constants; | ||
| 25 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 26 | import javassist.ByteArrayClassPath; | ||
| 27 | import javassist.ClassPool; | ||
| 28 | import javassist.CtClass; | ||
| 29 | import javassist.NotFoundException; | ||
| 30 | import javassist.bytecode.Descriptor; | ||
| 31 | |||
| 32 | public class JarClassIterator implements Iterator<CtClass> { | 32 | public class JarClassIterator implements Iterator<CtClass> { |
| 33 | 33 | ||
| 34 | private JarFile jar; | 34 | private JarFile jar; |
| 35 | private Iterator<JarEntry> iter; | 35 | private Iterator<JarEntry> iter; |
| 36 | 36 | ||
| 37 | public JarClassIterator(JarFile jar) { | 37 | public JarClassIterator(JarFile jar) { |
| 38 | this.jar = jar; | 38 | this.jar = jar; |
| 39 | 39 | ||
| 40 | // get the jar entries that correspond to classes | 40 | // get the jar entries that correspond to classes |
| 41 | List<JarEntry> classEntries = Lists.newArrayList(); | 41 | List<JarEntry> classEntries = Lists.newArrayList(); |
| 42 | Enumeration<JarEntry> entries = this.jar.entries(); | 42 | Enumeration<JarEntry> entries = this.jar.entries(); |
| 43 | while (entries.hasMoreElements()) { | 43 | while (entries.hasMoreElements()) { |
| 44 | JarEntry entry = entries.nextElement(); | 44 | JarEntry entry = entries.nextElement(); |
| 45 | 45 | ||
| 46 | // is this a class file? | 46 | // is this a class file? |
| 47 | if (entry.getName().endsWith(".class")) { | 47 | if (entry.getName().endsWith(".class")) { |
| 48 | classEntries.add(entry); | 48 | classEntries.add(entry); |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | this.iter = classEntries.iterator(); | 51 | this.iter = classEntries.iterator(); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | @Override | 54 | public static List<ClassEntry> getClassEntries(JarFile jar) { |
| 55 | public boolean hasNext() { | 55 | List<ClassEntry> classEntries = Lists.newArrayList(); |
| 56 | return this.iter.hasNext(); | 56 | Enumeration<JarEntry> entries = jar.entries(); |
| 57 | } | 57 | while (entries.hasMoreElements()) { |
| 58 | 58 | JarEntry entry = entries.nextElement(); | |
| 59 | @Override | 59 | |
| 60 | public CtClass next() { | 60 | // is this a class file? |
| 61 | JarEntry entry = this.iter.next(); | 61 | if (!entry.isDirectory() && entry.getName().endsWith(".class")) { |
| 62 | try { | 62 | classEntries.add(getClassEntry(entry)); |
| 63 | return getClass(this.jar, entry); | 63 | } |
| 64 | } catch (IOException | NotFoundException ex) { | 64 | } |
| 65 | throw new Error("Unable to load class: " + entry.getName()); | 65 | return classEntries; |
| 66 | } | 66 | } |
| 67 | } | 67 | |
| 68 | 68 | public static Iterable<CtClass> classes(final JarFile jar) { | |
| 69 | @Override | 69 | return () -> new JarClassIterator(jar); |
| 70 | public void remove() { | 70 | } |
| 71 | throw new UnsupportedOperationException(); | 71 | |
| 72 | } | 72 | private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { |
| 73 | 73 | // read the class into a buffer | |
| 74 | public static List<ClassEntry> getClassEntries(JarFile jar) { | 74 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 75 | List<ClassEntry> classEntries = Lists.newArrayList(); | 75 | byte[] buf = new byte[Constants.KiB]; |
| 76 | Enumeration<JarEntry> entries = jar.entries(); | 76 | int totalNumBytesRead = 0; |
| 77 | while (entries.hasMoreElements()) { | 77 | InputStream in = jar.getInputStream(entry); |
| 78 | JarEntry entry = entries.nextElement(); | 78 | while (in.available() > 0) { |
| 79 | 79 | int numBytesRead = in.read(buf); | |
| 80 | // is this a class file? | 80 | if (numBytesRead < 0) { |
| 81 | if (!entry.isDirectory() && entry.getName().endsWith(".class")) { | 81 | break; |
| 82 | classEntries.add(getClassEntry(entry)); | 82 | } |
| 83 | } | 83 | bos.write(buf, 0, numBytesRead); |
| 84 | } | 84 | |
| 85 | return classEntries; | 85 | // sanity checking |
| 86 | } | 86 | totalNumBytesRead += numBytesRead; |
| 87 | 87 | if (totalNumBytesRead > Constants.MiB) { | |
| 88 | public static Iterable<CtClass> classes(final JarFile jar) { | 88 | throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); |
| 89 | return () -> new JarClassIterator(jar); | 89 | } |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | private static CtClass getClass(JarFile jar, JarEntry entry) throws IOException, NotFoundException { | 92 | // get a javassist handle for the class |
| 93 | // read the class into a buffer | 93 | String className = Descriptor.toJavaName(getClassEntry(entry).getName()); |
| 94 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 94 | ClassPool classPool = new ClassPool(); |
| 95 | byte[] buf = new byte[Constants.KiB]; | 95 | classPool.appendSystemPath(); |
| 96 | int totalNumBytesRead = 0; | 96 | classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); |
| 97 | InputStream in = jar.getInputStream(entry); | 97 | return classPool.get(className); |
| 98 | while (in.available() > 0) { | 98 | } |
| 99 | int numBytesRead = in.read(buf); | 99 | |
| 100 | if (numBytesRead < 0) { | 100 | private static ClassEntry getClassEntry(JarEntry entry) { |
| 101 | break; | 101 | return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); |
| 102 | } | 102 | } |
| 103 | bos.write(buf, 0, numBytesRead); | 103 | |
| 104 | 104 | @Override | |
| 105 | // sanity checking | 105 | public boolean hasNext() { |
| 106 | totalNumBytesRead += numBytesRead; | 106 | return this.iter.hasNext(); |
| 107 | if (totalNumBytesRead > Constants.MiB) { | 107 | } |
| 108 | throw new Error("Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!"); | 108 | |
| 109 | } | 109 | @Override |
| 110 | } | 110 | public CtClass next() { |
| 111 | 111 | JarEntry entry = this.iter.next(); | |
| 112 | // get a javassist handle for the class | 112 | try { |
| 113 | String className = Descriptor.toJavaName(getClassEntry(entry).getName()); | 113 | return getClass(this.jar, entry); |
| 114 | ClassPool classPool = new ClassPool(); | 114 | } catch (IOException | NotFoundException ex) { |
| 115 | classPool.appendSystemPath(); | 115 | throw new Error("Unable to load class: " + entry.getName()); |
| 116 | classPool.insertClassPath(new ByteArrayClassPath(className, bos.toByteArray())); | 116 | } |
| 117 | return classPool.get(className); | 117 | } |
| 118 | } | 118 | |
| 119 | 119 | @Override | |
| 120 | private static ClassEntry getClassEntry(JarEntry entry) { | 120 | public void remove() { |
| 121 | return new ClassEntry(entry.getName().substring(0, entry.getName().length() - ".class".length())); | 121 | throw new UnsupportedOperationException(); |
| 122 | } | 122 | } |
| 123 | } | 123 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java index e8f74cc5..ea87dda5 100644 --- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -8,825 +8,824 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| 14 | |||
| 15 | import java.lang.reflect.Modifier; | ||
| 16 | import java.util.*; | ||
| 17 | import java.util.jar.JarFile; | ||
| 18 | |||
| 19 | import cuchaz.enigma.mapping.*; | 15 | import cuchaz.enigma.mapping.*; |
| 20 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.mapping.Translator; |
| 21 | import javassist.*; | 17 | import javassist.*; |
| 22 | import javassist.bytecode.*; | 18 | import javassist.bytecode.*; |
| 23 | import javassist.expr.*; | 19 | import javassist.expr.*; |
| 24 | 20 | ||
| 21 | import java.lang.reflect.Modifier; | ||
| 22 | import java.util.*; | ||
| 23 | import java.util.jar.JarFile; | ||
| 24 | |||
| 25 | public class JarIndex { | 25 | public class JarIndex { |
| 26 | 26 | ||
| 27 | private Set<ClassEntry> obfClassEntries; | 27 | private Set<ClassEntry> obfClassEntries; |
| 28 | private TranslationIndex translationIndex; | 28 | private TranslationIndex translationIndex; |
| 29 | private Map<Entry, Access> access; | 29 | private Map<Entry, Access> access; |
| 30 | private Multimap<ClassEntry, FieldEntry> fields; | 30 | private Multimap<ClassEntry, FieldEntry> fields; |
| 31 | private Multimap<ClassEntry, BehaviorEntry> behaviors; | 31 | private Multimap<ClassEntry, BehaviorEntry> behaviors; |
| 32 | private Multimap<String, MethodEntry> methodImplementations; | 32 | private Multimap<String, MethodEntry> methodImplementations; |
| 33 | private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; | 33 | private Multimap<BehaviorEntry, EntryReference<BehaviorEntry, BehaviorEntry>> behaviorReferences; |
| 34 | private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; | 34 | private Multimap<FieldEntry, EntryReference<FieldEntry, BehaviorEntry>> fieldReferences; |
| 35 | private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; | 35 | private Multimap<ClassEntry, ClassEntry> innerClassesByOuter; |
| 36 | private Map<ClassEntry, ClassEntry> outerClassesByInner; | 36 | private Map<ClassEntry, ClassEntry> outerClassesByInner; |
| 37 | private Map<ClassEntry, BehaviorEntry> anonymousClasses; | 37 | private Map<ClassEntry, BehaviorEntry> anonymousClasses; |
| 38 | private Map<MethodEntry, MethodEntry> bridgedMethods; | 38 | private Map<MethodEntry, MethodEntry> bridgedMethods; |
| 39 | private Set<MethodEntry> syntheticMethods; | 39 | private Set<MethodEntry> syntheticMethods; |
| 40 | 40 | ||
| 41 | public JarIndex() { | 41 | public JarIndex() { |
| 42 | this.obfClassEntries = Sets.newHashSet(); | 42 | this.obfClassEntries = Sets.newHashSet(); |
| 43 | this.translationIndex = new TranslationIndex(); | 43 | this.translationIndex = new TranslationIndex(); |
| 44 | this.access = Maps.newHashMap(); | 44 | this.access = Maps.newHashMap(); |
| 45 | this.fields = HashMultimap.create(); | 45 | this.fields = HashMultimap.create(); |
| 46 | this.behaviors = HashMultimap.create(); | 46 | this.behaviors = HashMultimap.create(); |
| 47 | this.methodImplementations = HashMultimap.create(); | 47 | this.methodImplementations = HashMultimap.create(); |
| 48 | this.behaviorReferences = HashMultimap.create(); | 48 | this.behaviorReferences = HashMultimap.create(); |
| 49 | this.fieldReferences = HashMultimap.create(); | 49 | this.fieldReferences = HashMultimap.create(); |
| 50 | this.innerClassesByOuter = HashMultimap.create(); | 50 | this.innerClassesByOuter = HashMultimap.create(); |
| 51 | this.outerClassesByInner = Maps.newHashMap(); | 51 | this.outerClassesByInner = Maps.newHashMap(); |
| 52 | this.anonymousClasses = Maps.newHashMap(); | 52 | this.anonymousClasses = Maps.newHashMap(); |
| 53 | this.bridgedMethods = Maps.newHashMap(); | 53 | this.bridgedMethods = Maps.newHashMap(); |
| 54 | this.syntheticMethods = Sets.newHashSet(); | 54 | this.syntheticMethods = Sets.newHashSet(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | public void indexJar(JarFile jar, boolean buildInnerClasses) { | 57 | public void indexJar(JarFile jar, boolean buildInnerClasses) { |
| 58 | 58 | ||
| 59 | // step 1: read the class names | 59 | // step 1: read the class names |
| 60 | this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); | 60 | this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar)); |
| 61 | 61 | ||
| 62 | // step 2: index field/method/constructor access | 62 | // step 2: index field/method/constructor access |
| 63 | for (CtClass c : JarClassIterator.classes(jar)) { | 63 | for (CtClass c : JarClassIterator.classes(jar)) { |
| 64 | for (CtField field : c.getDeclaredFields()) { | 64 | for (CtField field : c.getDeclaredFields()) { |
| 65 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); | 65 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); |
| 66 | this.access.put(fieldEntry, Access.get(field)); | 66 | this.access.put(fieldEntry, Access.get(field)); |
| 67 | this.fields.put(fieldEntry.getClassEntry(), fieldEntry); | 67 | this.fields.put(fieldEntry.getClassEntry(), fieldEntry); |
| 68 | } | 68 | } |
| 69 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 69 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 70 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 70 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 71 | this.access.put(behaviorEntry, Access.get(behavior)); | 71 | this.access.put(behaviorEntry, Access.get(behavior)); |
| 72 | this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); | 72 | this.behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); |
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // step 3: index extends, implements, fields, and methods | 76 | // step 3: index extends, implements, fields, and methods |
| 77 | for (CtClass c : JarClassIterator.classes(jar)) { | 77 | for (CtClass c : JarClassIterator.classes(jar)) { |
| 78 | this.translationIndex.indexClass(c); | 78 | this.translationIndex.indexClass(c); |
| 79 | String className = Descriptor.toJvmName(c.getName()); | 79 | String className = Descriptor.toJvmName(c.getName()); |
| 80 | for (String interfaceName : c.getClassFile().getInterfaces()) { | 80 | for (String interfaceName : c.getClassFile().getInterfaces()) { |
| 81 | className = Descriptor.toJvmName(className); | 81 | className = Descriptor.toJvmName(className); |
| 82 | interfaceName = Descriptor.toJvmName(interfaceName); | 82 | interfaceName = Descriptor.toJvmName(interfaceName); |
| 83 | if (className.equals(interfaceName)) { | 83 | if (className.equals(interfaceName)) { |
| 84 | throw new IllegalArgumentException("Class cannot be its own interface! " + className); | 84 | throw new IllegalArgumentException("Class cannot be its own interface! " + className); |
| 85 | } | 85 | } |
| 86 | } | 86 | } |
| 87 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 87 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 88 | indexBehavior(behavior); | 88 | indexBehavior(behavior); |
| 89 | } | 89 | } |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | // step 4: index field, method, constructor references | 92 | // step 4: index field, method, constructor references |
| 93 | for (CtClass c : JarClassIterator.classes(jar)) { | 93 | for (CtClass c : JarClassIterator.classes(jar)) { |
| 94 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 94 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 95 | indexBehaviorReferences(behavior); | 95 | indexBehaviorReferences(behavior); |
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | if (buildInnerClasses) { | 99 | if (buildInnerClasses) { |
| 100 | 100 | ||
| 101 | // step 5: index inner classes and anonymous classes | 101 | // step 5: index inner classes and anonymous classes |
| 102 | for (CtClass c : JarClassIterator.classes(jar)) { | 102 | for (CtClass c : JarClassIterator.classes(jar)) { |
| 103 | ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); | 103 | ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); |
| 104 | ClassEntry outerClassEntry = findOuterClass(c); | 104 | ClassEntry outerClassEntry = findOuterClass(c); |
| 105 | if (outerClassEntry != null) { | 105 | if (outerClassEntry != null) { |
| 106 | this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); | 106 | this.innerClassesByOuter.put(outerClassEntry, innerClassEntry); |
| 107 | boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; | 107 | boolean innerWasAdded = this.outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; |
| 108 | assert (innerWasAdded); | 108 | assert (innerWasAdded); |
| 109 | 109 | ||
| 110 | BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); | 110 | BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); |
| 111 | if (enclosingBehavior != null) { | 111 | if (enclosingBehavior != null) { |
| 112 | this.anonymousClasses.put(innerClassEntry, enclosingBehavior); | 112 | this.anonymousClasses.put(innerClassEntry, enclosingBehavior); |
| 113 | 113 | ||
| 114 | // DEBUG | 114 | // DEBUG |
| 115 | //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); | 115 | //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); |
| 116 | }/* else { | 116 | }/* else { |
| 117 | // DEBUG | 117 | // DEBUG |
| 118 | //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); | 118 | //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); |
| 119 | }*/ | 119 | }*/ |
| 120 | } | 120 | } |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | // step 6: update other indices with inner class info | 123 | // step 6: update other indices with inner class info |
| 124 | Map<String, String> renames = Maps.newHashMap(); | 124 | Map<String, String> renames = Maps.newHashMap(); |
| 125 | for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { | 125 | for (ClassEntry innerClassEntry : this.innerClassesByOuter.values()) { |
| 126 | String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); | 126 | String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); |
| 127 | if (!innerClassEntry.getName().equals(newName)) { | 127 | if (!innerClassEntry.getName().equals(newName)) { |
| 128 | // DEBUG | 128 | // DEBUG |
| 129 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); | 129 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); |
| 130 | renames.put(innerClassEntry.getName(), newName); | 130 | renames.put(innerClassEntry.getName(), newName); |
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); | 133 | EntryRenamer.renameClassesInSet(renames, this.obfClassEntries); |
| 134 | this.translationIndex.renameClasses(renames); | 134 | this.translationIndex.renameClasses(renames); |
| 135 | EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); | 135 | EntryRenamer.renameClassesInMultimap(renames, this.methodImplementations); |
| 136 | EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); | 136 | EntryRenamer.renameClassesInMultimap(renames, this.behaviorReferences); |
| 137 | EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); | 137 | EntryRenamer.renameClassesInMultimap(renames, this.fieldReferences); |
| 138 | EntryRenamer.renameClassesInMap(renames, this.access); | 138 | EntryRenamer.renameClassesInMap(renames, this.access); |
| 139 | } | 139 | } |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | private void indexBehavior(CtBehavior behavior) { | 142 | private void indexBehavior(CtBehavior behavior) { |
| 143 | // get the behavior entry | 143 | // get the behavior entry |
| 144 | final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 144 | final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 145 | if (behaviorEntry instanceof MethodEntry) { | 145 | if (behaviorEntry instanceof MethodEntry) { |
| 146 | MethodEntry methodEntry = (MethodEntry) behaviorEntry; | 146 | MethodEntry methodEntry = (MethodEntry) behaviorEntry; |
| 147 | 147 | ||
| 148 | // is synthetic | 148 | // is synthetic |
| 149 | if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { | 149 | if ((behavior.getModifiers() & AccessFlag.SYNTHETIC) != 0) { |
| 150 | syntheticMethods.add(methodEntry); | 150 | syntheticMethods.add(methodEntry); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | // index implementation | 153 | // index implementation |
| 154 | this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); | 154 | this.methodImplementations.put(behaviorEntry.getClassName(), methodEntry); |
| 155 | 155 | ||
| 156 | // look for bridge and bridged methods | 156 | // look for bridge and bridged methods |
| 157 | CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); | 157 | CtMethod bridgedMethod = getBridgedMethod((CtMethod) behavior); |
| 158 | if (bridgedMethod != null) { | 158 | if (bridgedMethod != null) { |
| 159 | this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); | 159 | this.bridgedMethods.put(methodEntry, EntryFactory.getMethodEntry(bridgedMethod)); |
| 160 | } | 160 | } |
| 161 | } | 161 | } |
| 162 | // looks like we don't care about constructors here | 162 | // looks like we don't care about constructors here |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | private void indexBehaviorReferences(CtBehavior behavior) { | 165 | private void indexBehaviorReferences(CtBehavior behavior) { |
| 166 | // index method calls | 166 | // index method calls |
| 167 | final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 167 | final BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 168 | try { | 168 | try { |
| 169 | behavior.instrument(new ExprEditor() { | 169 | behavior.instrument(new ExprEditor() { |
| 170 | @Override | 170 | @Override |
| 171 | public void edit(MethodCall call) { | 171 | public void edit(MethodCall call) { |
| 172 | MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); | 172 | MethodEntry calledMethodEntry = EntryFactory.getMethodEntry(call); |
| 173 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); | 173 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledMethodEntry); |
| 174 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { | 174 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { |
| 175 | calledMethodEntry = new MethodEntry( | 175 | calledMethodEntry = new MethodEntry( |
| 176 | resolvedClassEntry, | 176 | resolvedClassEntry, |
| 177 | calledMethodEntry.getName(), | 177 | calledMethodEntry.getName(), |
| 178 | calledMethodEntry.getSignature() | 178 | calledMethodEntry.getSignature() |
| 179 | ); | 179 | ); |
| 180 | } | 180 | } |
| 181 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( | 181 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( |
| 182 | calledMethodEntry, | 182 | calledMethodEntry, |
| 183 | call.getMethodName(), | 183 | call.getMethodName(), |
| 184 | behaviorEntry | 184 | behaviorEntry |
| 185 | ); | 185 | ); |
| 186 | behaviorReferences.put(calledMethodEntry, reference); | 186 | behaviorReferences.put(calledMethodEntry, reference); |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | @Override | 189 | @Override |
| 190 | public void edit(FieldAccess call) { | 190 | public void edit(FieldAccess call) { |
| 191 | FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); | 191 | FieldEntry calledFieldEntry = EntryFactory.getFieldEntry(call); |
| 192 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); | 192 | ClassEntry resolvedClassEntry = translationIndex.resolveEntryClass(calledFieldEntry); |
| 193 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { | 193 | if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { |
| 194 | calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); | 194 | calledFieldEntry = new FieldEntry(calledFieldEntry, resolvedClassEntry); |
| 195 | } | 195 | } |
| 196 | EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>( | 196 | EntryReference<FieldEntry, BehaviorEntry> reference = new EntryReference<>( |
| 197 | calledFieldEntry, | 197 | calledFieldEntry, |
| 198 | call.getFieldName(), | 198 | call.getFieldName(), |
| 199 | behaviorEntry | 199 | behaviorEntry |
| 200 | ); | 200 | ); |
| 201 | fieldReferences.put(calledFieldEntry, reference); | 201 | fieldReferences.put(calledFieldEntry, reference); |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | @Override | 204 | @Override |
| 205 | public void edit(ConstructorCall call) { | 205 | public void edit(ConstructorCall call) { |
| 206 | ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); | 206 | ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); |
| 207 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( | 207 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( |
| 208 | calledConstructorEntry, | 208 | calledConstructorEntry, |
| 209 | call.getMethodName(), | 209 | call.getMethodName(), |
| 210 | behaviorEntry | 210 | behaviorEntry |
| 211 | ); | 211 | ); |
| 212 | behaviorReferences.put(calledConstructorEntry, reference); | 212 | behaviorReferences.put(calledConstructorEntry, reference); |
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | @Override | 215 | @Override |
| 216 | public void edit(NewExpr call) { | 216 | public void edit(NewExpr call) { |
| 217 | ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); | 217 | ConstructorEntry calledConstructorEntry = EntryFactory.getConstructorEntry(call); |
| 218 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( | 218 | EntryReference<BehaviorEntry, BehaviorEntry> reference = new EntryReference<>( |
| 219 | calledConstructorEntry, | 219 | calledConstructorEntry, |
| 220 | call.getClassName(), | 220 | call.getClassName(), |
| 221 | behaviorEntry | 221 | behaviorEntry |
| 222 | ); | 222 | ); |
| 223 | behaviorReferences.put(calledConstructorEntry, reference); | 223 | behaviorReferences.put(calledConstructorEntry, reference); |
| 224 | } | 224 | } |
| 225 | }); | 225 | }); |
| 226 | } catch (CannotCompileException ex) { | 226 | } catch (CannotCompileException ex) { |
| 227 | throw new Error(ex); | 227 | throw new Error(ex); |
| 228 | } | 228 | } |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | private CtMethod getBridgedMethod(CtMethod method) { | 231 | private CtMethod getBridgedMethod(CtMethod method) { |
| 232 | 232 | ||
| 233 | // bridge methods just call another method, cast it to the return type, and return the result | 233 | // bridge methods just call another method, cast it to the return type, and return the result |
| 234 | // let's see if we can detect this scenario | 234 | // let's see if we can detect this scenario |
| 235 | 235 | ||
| 236 | // skip non-synthetic methods | 236 | // skip non-synthetic methods |
| 237 | if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { | 237 | if ((method.getModifiers() & AccessFlag.SYNTHETIC) == 0) { |
| 238 | return null; | 238 | return null; |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | // get all the called methods | 241 | // get all the called methods |
| 242 | final List<MethodCall> methodCalls = Lists.newArrayList(); | 242 | final List<MethodCall> methodCalls = Lists.newArrayList(); |
| 243 | try { | 243 | try { |
| 244 | method.instrument(new ExprEditor() { | 244 | method.instrument(new ExprEditor() { |
| 245 | @Override | 245 | @Override |
| 246 | public void edit(MethodCall call) { | 246 | public void edit(MethodCall call) { |
| 247 | methodCalls.add(call); | 247 | methodCalls.add(call); |
| 248 | } | 248 | } |
| 249 | }); | 249 | }); |
| 250 | } catch (CannotCompileException ex) { | 250 | } catch (CannotCompileException ex) { |
| 251 | // this is stupid... we're not even compiling anything | 251 | // this is stupid... we're not even compiling anything |
| 252 | throw new Error(ex); | 252 | throw new Error(ex); |
| 253 | } | 253 | } |
| 254 | 254 | ||
| 255 | // is there just one? | 255 | // is there just one? |
| 256 | if (methodCalls.size() != 1) { | 256 | if (methodCalls.size() != 1) { |
| 257 | return null; | 257 | return null; |
| 258 | } | 258 | } |
| 259 | MethodCall call = methodCalls.get(0); | 259 | MethodCall call = methodCalls.get(0); |
| 260 | 260 | ||
| 261 | try { | 261 | try { |
| 262 | // we have a bridge method! | 262 | // we have a bridge method! |
| 263 | return call.getMethod(); | 263 | return call.getMethod(); |
| 264 | } catch (NotFoundException ex) { | 264 | } catch (NotFoundException ex) { |
| 265 | // can't find the type? not a bridge method | 265 | // can't find the type? not a bridge method |
| 266 | return null; | 266 | return null; |
| 267 | } | 267 | } |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | private ClassEntry findOuterClass(CtClass c) { | 270 | private ClassEntry findOuterClass(CtClass c) { |
| 271 | 271 | ||
| 272 | ClassEntry classEntry = EntryFactory.getClassEntry(c); | 272 | ClassEntry classEntry = EntryFactory.getClassEntry(c); |
| 273 | 273 | ||
| 274 | // does this class already have an outer class? | 274 | // does this class already have an outer class? |
| 275 | if (classEntry.isInnerClass()) { | 275 | if (classEntry.isInnerClass()) { |
| 276 | return classEntry.getOuterClassEntry(); | 276 | return classEntry.getOuterClassEntry(); |
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | // inner classes: | 279 | // inner classes: |
| 280 | // have constructors that can (illegally) set synthetic fields | 280 | // have constructors that can (illegally) set synthetic fields |
| 281 | // the outer class is the only class that calls constructors | 281 | // the outer class is the only class that calls constructors |
| 282 | 282 | ||
| 283 | // use the synthetic fields to find the synthetic constructors | 283 | // use the synthetic fields to find the synthetic constructors |
| 284 | for (CtConstructor constructor : c.getDeclaredConstructors()) { | 284 | for (CtConstructor constructor : c.getDeclaredConstructors()) { |
| 285 | Set<String> syntheticFieldTypes = Sets.newHashSet(); | 285 | Set<String> syntheticFieldTypes = Sets.newHashSet(); |
| 286 | if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { | 286 | if (!isIllegalConstructor(syntheticFieldTypes, constructor)) { |
| 287 | continue; | 287 | continue; |
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); | 290 | ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); |
| 291 | 291 | ||
| 292 | // gather the classes from the illegally-set synthetic fields | 292 | // gather the classes from the illegally-set synthetic fields |
| 293 | Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); | 293 | Set<ClassEntry> illegallySetClasses = Sets.newHashSet(); |
| 294 | for (String type : syntheticFieldTypes) { | 294 | for (String type : syntheticFieldTypes) { |
| 295 | if (type.startsWith("L")) { | 295 | if (type.startsWith("L")) { |
| 296 | ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); | 296 | ClassEntry outerClassEntry = new ClassEntry(type.substring(1, type.length() - 1)); |
| 297 | if (isSaneOuterClass(outerClassEntry, classEntry)) { | 297 | if (isSaneOuterClass(outerClassEntry, classEntry)) { |
| 298 | illegallySetClasses.add(outerClassEntry); | 298 | illegallySetClasses.add(outerClassEntry); |
| 299 | } | 299 | } |
| 300 | } | 300 | } |
| 301 | } | 301 | } |
| 302 | 302 | ||
| 303 | // who calls this constructor? | 303 | // who calls this constructor? |
| 304 | Set<ClassEntry> callerClasses = Sets.newHashSet(); | 304 | Set<ClassEntry> callerClasses = Sets.newHashSet(); |
| 305 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) { | 305 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : getBehaviorReferences(constructorEntry)) { |
| 306 | 306 | ||
| 307 | // make sure it's not a call to super | 307 | // make sure it's not a call to super |
| 308 | if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { | 308 | if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { |
| 309 | 309 | ||
| 310 | // is the entry a superclass of the context? | 310 | // is the entry a superclass of the context? |
| 311 | ClassEntry calledClassEntry = reference.entry.getClassEntry(); | 311 | ClassEntry calledClassEntry = reference.entry.getClassEntry(); |
| 312 | ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); | 312 | ClassEntry superclassEntry = this.translationIndex.getSuperclass(reference.context.getClassEntry()); |
| 313 | if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { | 313 | if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) { |
| 314 | // it's a super call, skip | 314 | // it's a super call, skip |
| 315 | continue; | 315 | continue; |
| 316 | } | 316 | } |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { | 319 | if (isSaneOuterClass(reference.context.getClassEntry(), classEntry)) { |
| 320 | callerClasses.add(reference.context.getClassEntry()); | 320 | callerClasses.add(reference.context.getClassEntry()); |
| 321 | } | 321 | } |
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | // do we have an answer yet? | 324 | // do we have an answer yet? |
| 325 | if (callerClasses.isEmpty()) { | 325 | if (callerClasses.isEmpty()) { |
| 326 | if (illegallySetClasses.size() == 1) { | 326 | if (illegallySetClasses.size() == 1) { |
| 327 | return illegallySetClasses.iterator().next(); | 327 | return illegallySetClasses.iterator().next(); |
| 328 | } else { | 328 | } else { |
| 329 | System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); | 329 | System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); |
| 330 | } | 330 | } |
| 331 | } else { | 331 | } else { |
| 332 | if (callerClasses.size() == 1) { | 332 | if (callerClasses.size() == 1) { |
| 333 | return callerClasses.iterator().next(); | 333 | return callerClasses.iterator().next(); |
| 334 | } else { | 334 | } else { |
| 335 | // multiple callers, do the illegally set classes narrow it down? | 335 | // multiple callers, do the illegally set classes narrow it down? |
| 336 | Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); | 336 | Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); |
| 337 | intersection.retainAll(illegallySetClasses); | 337 | intersection.retainAll(illegallySetClasses); |
| 338 | if (intersection.size() == 1) { | 338 | if (intersection.size() == 1) { |
| 339 | return intersection.iterator().next(); | 339 | return intersection.iterator().next(); |
| 340 | } else { | 340 | } else { |
| 341 | System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); | 341 | System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); |
| 342 | } | 342 | } |
| 343 | } | 343 | } |
| 344 | } | 344 | } |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | return null; | 347 | return null; |
| 348 | } | 348 | } |
| 349 | 349 | ||
| 350 | private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { | 350 | private boolean isSaneOuterClass(ClassEntry outerClassEntry, ClassEntry innerClassEntry) { |
| 351 | 351 | ||
| 352 | // clearly this would be silly | 352 | // clearly this would be silly |
| 353 | if (outerClassEntry.equals(innerClassEntry)) { | 353 | if (outerClassEntry.equals(innerClassEntry)) { |
| 354 | return false; | 354 | return false; |
| 355 | } | 355 | } |
| 356 | 356 | ||
| 357 | // is the outer class in the jar? | 357 | // is the outer class in the jar? |
| 358 | return this.obfClassEntries.contains(outerClassEntry); | 358 | return this.obfClassEntries.contains(outerClassEntry); |
| 359 | 359 | ||
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | @SuppressWarnings("unchecked") | 362 | @SuppressWarnings("unchecked") |
| 363 | private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { | 363 | private boolean isIllegalConstructor(Set<String> syntheticFieldTypes, CtConstructor constructor) { |
| 364 | 364 | ||
| 365 | // illegal constructors only set synthetic member fields, then call super() | 365 | // illegal constructors only set synthetic member fields, then call super() |
| 366 | String className = constructor.getDeclaringClass().getName(); | 366 | String className = constructor.getDeclaringClass().getName(); |
| 367 | 367 | ||
| 368 | // collect all the field accesses, constructor calls, and method calls | 368 | // collect all the field accesses, constructor calls, and method calls |
| 369 | final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); | 369 | final List<FieldAccess> illegalFieldWrites = Lists.newArrayList(); |
| 370 | final List<ConstructorCall> constructorCalls = Lists.newArrayList(); | 370 | final List<ConstructorCall> constructorCalls = Lists.newArrayList(); |
| 371 | try { | 371 | try { |
| 372 | constructor.instrument(new ExprEditor() { | 372 | constructor.instrument(new ExprEditor() { |
| 373 | @Override | 373 | @Override |
| 374 | public void edit(FieldAccess fieldAccess) { | 374 | public void edit(FieldAccess fieldAccess) { |
| 375 | if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { | 375 | if (fieldAccess.isWriter() && constructorCalls.isEmpty()) { |
| 376 | illegalFieldWrites.add(fieldAccess); | 376 | illegalFieldWrites.add(fieldAccess); |
| 377 | } | 377 | } |
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | @Override | 380 | @Override |
| 381 | public void edit(ConstructorCall constructorCall) { | 381 | public void edit(ConstructorCall constructorCall) { |
| 382 | constructorCalls.add(constructorCall); | 382 | constructorCalls.add(constructorCall); |
| 383 | } | 383 | } |
| 384 | }); | 384 | }); |
| 385 | } catch (CannotCompileException ex) { | 385 | } catch (CannotCompileException ex) { |
| 386 | // we're not compiling anything... this is stupid | 386 | // we're not compiling anything... this is stupid |
| 387 | throw new Error(ex); | 387 | throw new Error(ex); |
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | // are there any illegal field writes? | 390 | // are there any illegal field writes? |
| 391 | if (illegalFieldWrites.isEmpty()) { | 391 | if (illegalFieldWrites.isEmpty()) { |
| 392 | return false; | 392 | return false; |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | // are all the writes to synthetic fields? | 395 | // are all the writes to synthetic fields? |
| 396 | for (FieldAccess fieldWrite : illegalFieldWrites) { | 396 | for (FieldAccess fieldWrite : illegalFieldWrites) { |
| 397 | 397 | ||
| 398 | // all illegal writes have to be to the local class | 398 | // all illegal writes have to be to the local class |
| 399 | if (!fieldWrite.getClassName().equals(className)) { | 399 | if (!fieldWrite.getClassName().equals(className)) { |
| 400 | System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); | 400 | System.err.println(String.format("WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName())); |
| 401 | return false; | 401 | return false; |
| 402 | } | 402 | } |
| 403 | 403 | ||
| 404 | // find the field | 404 | // find the field |
| 405 | FieldInfo fieldInfo = null; | 405 | FieldInfo fieldInfo = null; |
| 406 | for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) { | 406 | for (FieldInfo info : (List<FieldInfo>) constructor.getDeclaringClass().getClassFile().getFields()) { |
| 407 | if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { | 407 | if (info.getName().equals(fieldWrite.getFieldName()) && info.getDescriptor().equals(fieldWrite.getSignature())) { |
| 408 | fieldInfo = info; | 408 | fieldInfo = info; |
| 409 | break; | 409 | break; |
| 410 | } | 410 | } |
| 411 | } | 411 | } |
| 412 | if (fieldInfo == null) { | 412 | if (fieldInfo == null) { |
| 413 | // field is in a superclass or something, can't be a local synthetic member | 413 | // field is in a superclass or something, can't be a local synthetic member |
| 414 | return false; | 414 | return false; |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | // is this field synthetic? | 417 | // is this field synthetic? |
| 418 | boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; | 418 | boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; |
| 419 | if (isSynthetic) { | 419 | if (isSynthetic) { |
| 420 | syntheticFieldTypes.add(fieldInfo.getDescriptor()); | 420 | syntheticFieldTypes.add(fieldInfo.getDescriptor()); |
| 421 | } else { | 421 | } else { |
| 422 | System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); | 422 | System.err.println(String.format("WARNING: illegal write to non synthetic field %s %s.%s", fieldInfo.getDescriptor(), className, fieldInfo.getName())); |
| 423 | return false; | 423 | return false; |
| 424 | } | 424 | } |
| 425 | } | 425 | } |
| 426 | 426 | ||
| 427 | // we passed all the tests! | 427 | // we passed all the tests! |
| 428 | return true; | 428 | return true; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { | 431 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { |
| 432 | 432 | ||
| 433 | // is this class already marked anonymous? | 433 | // is this class already marked anonymous? |
| 434 | EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); | 434 | EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); |
| 435 | if (enclosingMethodAttribute != null) { | 435 | if (enclosingMethodAttribute != null) { |
| 436 | if (enclosingMethodAttribute.methodIndex() > 0) { | 436 | if (enclosingMethodAttribute.methodIndex() > 0) { |
| 437 | return EntryFactory.getBehaviorEntry( | 437 | return EntryFactory.getBehaviorEntry( |
| 438 | Descriptor.toJvmName(enclosingMethodAttribute.className()), | 438 | Descriptor.toJvmName(enclosingMethodAttribute.className()), |
| 439 | enclosingMethodAttribute.methodName(), | 439 | enclosingMethodAttribute.methodName(), |
| 440 | enclosingMethodAttribute.methodDescriptor() | 440 | enclosingMethodAttribute.methodDescriptor() |
| 441 | ); | 441 | ); |
| 442 | } else { | 442 | } else { |
| 443 | // an attribute but no method? assume not anonymous | 443 | // an attribute but no method? assume not anonymous |
| 444 | return null; | 444 | return null; |
| 445 | } | 445 | } |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous | 448 | // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous |
| 449 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 449 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 450 | if (innerClassesAttribute != null) { | 450 | if (innerClassesAttribute != null) { |
| 451 | return null; | 451 | return null; |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 454 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 455 | 455 | ||
| 456 | // anonymous classes: | 456 | // anonymous classes: |
| 457 | // can't be abstract | 457 | // can't be abstract |
| 458 | // have only one constructor | 458 | // have only one constructor |
| 459 | // it's called exactly once by the outer class | 459 | // it's called exactly once by the outer class |
| 460 | // the type the instance is assigned to can't be this type | 460 | // the type the instance is assigned to can't be this type |
| 461 | 461 | ||
| 462 | // is abstract? | 462 | // is abstract? |
| 463 | if (Modifier.isAbstract(c.getModifiers())) { | 463 | if (Modifier.isAbstract(c.getModifiers())) { |
| 464 | return null; | 464 | return null; |
| 465 | } | 465 | } |
| 466 | 466 | ||
| 467 | // is there exactly one constructor? | 467 | // is there exactly one constructor? |
| 468 | if (c.getDeclaredConstructors().length != 1) { | 468 | if (c.getDeclaredConstructors().length != 1) { |
| 469 | return null; | 469 | return null; |
| 470 | } | 470 | } |
| 471 | CtConstructor constructor = c.getDeclaredConstructors()[0]; | 471 | CtConstructor constructor = c.getDeclaredConstructors()[0]; |
| 472 | 472 | ||
| 473 | // is this constructor called exactly once? | 473 | // is this constructor called exactly once? |
| 474 | ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); | 474 | ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); |
| 475 | Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry); | 475 | Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = getBehaviorReferences(constructorEntry); |
| 476 | if (references.size() != 1) { | 476 | if (references.size() != 1) { |
| 477 | return null; | 477 | return null; |
| 478 | } | 478 | } |
| 479 | 479 | ||
| 480 | // does the caller use this type? | 480 | // does the caller use this type? |
| 481 | BehaviorEntry caller = references.iterator().next().context; | 481 | BehaviorEntry caller = references.iterator().next().context; |
| 482 | for (FieldEntry fieldEntry : getReferencedFields(caller)) { | 482 | for (FieldEntry fieldEntry : getReferencedFields(caller)) { |
| 483 | if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { | 483 | if (fieldEntry.getType().hasClass() && fieldEntry.getType().getClassEntry().equals(innerClassEntry)) { |
| 484 | // caller references this type, so it can't be anonymous | 484 | // caller references this type, so it can't be anonymous |
| 485 | return null; | 485 | return null; |
| 486 | } | 486 | } |
| 487 | } | 487 | } |
| 488 | for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { | 488 | for (BehaviorEntry behaviorEntry : getReferencedBehaviors(caller)) { |
| 489 | if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { | 489 | if (behaviorEntry.getSignature().hasClass(innerClassEntry)) { |
| 490 | return null; | 490 | return null; |
| 491 | } | 491 | } |
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | return caller; | 494 | return caller; |
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | public Set<ClassEntry> getObfClassEntries() { | 497 | public Set<ClassEntry> getObfClassEntries() { |
| 498 | return this.obfClassEntries; | 498 | return this.obfClassEntries; |
| 499 | } | 499 | } |
| 500 | 500 | ||
| 501 | public Collection<FieldEntry> getObfFieldEntries() { | 501 | public Collection<FieldEntry> getObfFieldEntries() { |
| 502 | return this.fields.values(); | 502 | return this.fields.values(); |
| 503 | } | 503 | } |
| 504 | 504 | ||
| 505 | public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { | 505 | public Collection<FieldEntry> getObfFieldEntries(ClassEntry classEntry) { |
| 506 | return this.fields.get(classEntry); | 506 | return this.fields.get(classEntry); |
| 507 | } | 507 | } |
| 508 | 508 | ||
| 509 | public Collection<BehaviorEntry> getObfBehaviorEntries() { | 509 | public Collection<BehaviorEntry> getObfBehaviorEntries() { |
| 510 | return this.behaviors.values(); | 510 | return this.behaviors.values(); |
| 511 | } | 511 | } |
| 512 | 512 | ||
| 513 | public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { | 513 | public Collection<BehaviorEntry> getObfBehaviorEntries(ClassEntry classEntry) { |
| 514 | return this.behaviors.get(classEntry); | 514 | return this.behaviors.get(classEntry); |
| 515 | } | 515 | } |
| 516 | 516 | ||
| 517 | public TranslationIndex getTranslationIndex() { | 517 | public TranslationIndex getTranslationIndex() { |
| 518 | return this.translationIndex; | 518 | return this.translationIndex; |
| 519 | } | 519 | } |
| 520 | 520 | ||
| 521 | public Access getAccess(Entry entry) { | 521 | public Access getAccess(Entry entry) { |
| 522 | return this.access.get(entry); | 522 | return this.access.get(entry); |
| 523 | } | 523 | } |
| 524 | 524 | ||
| 525 | public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { | 525 | public ClassInheritanceTreeNode getClassInheritance(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { |
| 526 | 526 | ||
| 527 | // get the root node | 527 | // get the root node |
| 528 | List<String> ancestry = Lists.newArrayList(); | 528 | List<String> ancestry = Lists.newArrayList(); |
| 529 | ancestry.add(obfClassEntry.getName()); | 529 | ancestry.add(obfClassEntry.getName()); |
| 530 | for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { | 530 | for (ClassEntry classEntry : this.translationIndex.getAncestry(obfClassEntry)) { |
| 531 | if (containsObfClass(classEntry)) { | 531 | if (containsObfClass(classEntry)) { |
| 532 | ancestry.add(classEntry.getName()); | 532 | ancestry.add(classEntry.getName()); |
| 533 | } | 533 | } |
| 534 | } | 534 | } |
| 535 | ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( | 535 | ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( |
| 536 | deobfuscatingTranslator, | 536 | deobfuscatingTranslator, |
| 537 | ancestry.get(ancestry.size() - 1) | 537 | ancestry.get(ancestry.size() - 1) |
| 538 | ); | 538 | ); |
| 539 | 539 | ||
| 540 | // expand all children recursively | 540 | // expand all children recursively |
| 541 | rootNode.load(this.translationIndex, true); | 541 | rootNode.load(this.translationIndex, true); |
| 542 | 542 | ||
| 543 | return rootNode; | 543 | return rootNode; |
| 544 | } | 544 | } |
| 545 | 545 | ||
| 546 | public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { | 546 | public ClassImplementationsTreeNode getClassImplementations(Translator deobfuscatingTranslator, ClassEntry obfClassEntry) { |
| 547 | 547 | ||
| 548 | // is this even an interface? | 548 | // is this even an interface? |
| 549 | if (isInterface(obfClassEntry.getClassName())) { | 549 | if (isInterface(obfClassEntry.getClassName())) { |
| 550 | ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); | 550 | ClassImplementationsTreeNode node = new ClassImplementationsTreeNode(deobfuscatingTranslator, obfClassEntry); |
| 551 | node.load(this); | 551 | node.load(this); |
| 552 | return node; | 552 | return node; |
| 553 | } | 553 | } |
| 554 | return null; | 554 | return null; |
| 555 | } | 555 | } |
| 556 | 556 | ||
| 557 | public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { | 557 | public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { |
| 558 | 558 | ||
| 559 | // travel to the ancestor implementation | 559 | // travel to the ancestor implementation |
| 560 | ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); | 560 | ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry(); |
| 561 | for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { | 561 | for (ClassEntry ancestorClassEntry : this.translationIndex.getAncestry(obfMethodEntry.getClassEntry())) { |
| 562 | MethodEntry ancestorMethodEntry = new MethodEntry( | 562 | MethodEntry ancestorMethodEntry = new MethodEntry( |
| 563 | new ClassEntry(ancestorClassEntry), | 563 | new ClassEntry(ancestorClassEntry), |
| 564 | obfMethodEntry.getName(), | 564 | obfMethodEntry.getName(), |
| 565 | obfMethodEntry.getSignature() | 565 | obfMethodEntry.getSignature() |
| 566 | ); | 566 | ); |
| 567 | if (containsObfBehavior(ancestorMethodEntry)) { | 567 | if (containsObfBehavior(ancestorMethodEntry)) { |
| 568 | baseImplementationClassEntry = ancestorClassEntry; | 568 | baseImplementationClassEntry = ancestorClassEntry; |
| 569 | } | 569 | } |
| 570 | } | 570 | } |
| 571 | 571 | ||
| 572 | // make a root node at the base | 572 | // make a root node at the base |
| 573 | MethodEntry methodEntry = new MethodEntry( | 573 | MethodEntry methodEntry = new MethodEntry( |
| 574 | baseImplementationClassEntry, | 574 | baseImplementationClassEntry, |
| 575 | obfMethodEntry.getName(), | 575 | obfMethodEntry.getName(), |
| 576 | obfMethodEntry.getSignature() | 576 | obfMethodEntry.getSignature() |
| 577 | ); | 577 | ); |
| 578 | MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( | 578 | MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( |
| 579 | deobfuscatingTranslator, | 579 | deobfuscatingTranslator, |
| 580 | methodEntry, | 580 | methodEntry, |
| 581 | containsObfBehavior(methodEntry) | 581 | containsObfBehavior(methodEntry) |
| 582 | ); | 582 | ); |
| 583 | 583 | ||
| 584 | // expand the full tree | 584 | // expand the full tree |
| 585 | rootNode.load(this, true); | 585 | rootNode.load(this, true); |
| 586 | 586 | ||
| 587 | return rootNode; | 587 | return rootNode; |
| 588 | } | 588 | } |
| 589 | 589 | ||
| 590 | public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { | 590 | public List<MethodImplementationsTreeNode> getMethodImplementations(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { |
| 591 | 591 | ||
| 592 | List<MethodEntry> interfaceMethodEntries = Lists.newArrayList(); | 592 | List<MethodEntry> interfaceMethodEntries = Lists.newArrayList(); |
| 593 | 593 | ||
| 594 | // is this method on an interface? | 594 | // is this method on an interface? |
| 595 | if (isInterface(obfMethodEntry.getClassName())) { | 595 | if (isInterface(obfMethodEntry.getClassName())) { |
| 596 | interfaceMethodEntries.add(obfMethodEntry); | 596 | interfaceMethodEntries.add(obfMethodEntry); |
| 597 | } else { | 597 | } else { |
| 598 | // get the interface class | 598 | // get the interface class |
| 599 | for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { | 599 | for (ClassEntry interfaceEntry : getInterfaces(obfMethodEntry.getClassName())) { |
| 600 | 600 | ||
| 601 | // is this method defined in this interface? | 601 | // is this method defined in this interface? |
| 602 | MethodEntry methodInterface = new MethodEntry( | 602 | MethodEntry methodInterface = new MethodEntry( |
| 603 | interfaceEntry, | 603 | interfaceEntry, |
| 604 | obfMethodEntry.getName(), | 604 | obfMethodEntry.getName(), |
| 605 | obfMethodEntry.getSignature() | 605 | obfMethodEntry.getSignature() |
| 606 | ); | 606 | ); |
| 607 | if (containsObfBehavior(methodInterface)) { | 607 | if (containsObfBehavior(methodInterface)) { |
| 608 | interfaceMethodEntries.add(methodInterface); | 608 | interfaceMethodEntries.add(methodInterface); |
| 609 | } | 609 | } |
| 610 | } | 610 | } |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | 613 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); |
| 614 | if (!interfaceMethodEntries.isEmpty()) { | 614 | if (!interfaceMethodEntries.isEmpty()) { |
| 615 | for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { | 615 | for (MethodEntry interfaceMethodEntry : interfaceMethodEntries) { |
| 616 | MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); | 616 | MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(deobfuscatingTranslator, interfaceMethodEntry); |
| 617 | node.load(this); | 617 | node.load(this); |
| 618 | nodes.add(node); | 618 | nodes.add(node); |
| 619 | } | 619 | } |
| 620 | } | 620 | } |
| 621 | return nodes; | 621 | return nodes; |
| 622 | } | 622 | } |
| 623 | 623 | ||
| 624 | public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { | 624 | public Set<MethodEntry> getRelatedMethodImplementations(MethodEntry obfMethodEntry) { |
| 625 | Set<MethodEntry> methodEntries = Sets.newHashSet(); | 625 | Set<MethodEntry> methodEntries = Sets.newHashSet(); |
| 626 | getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); | 626 | getRelatedMethodImplementations(methodEntries, getMethodInheritance(new Translator(), obfMethodEntry)); |
| 627 | return methodEntries; | 627 | return methodEntries; |
| 628 | } | 628 | } |
| 629 | 629 | ||
| 630 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { | 630 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) { |
| 631 | MethodEntry methodEntry = node.getMethodEntry(); | 631 | MethodEntry methodEntry = node.getMethodEntry(); |
| 632 | 632 | ||
| 633 | if (containsObfBehavior(methodEntry)) { | 633 | if (containsObfBehavior(methodEntry)) { |
| 634 | // collect the entry | 634 | // collect the entry |
| 635 | methodEntries.add(methodEntry); | 635 | methodEntries.add(methodEntry); |
| 636 | } | 636 | } |
| 637 | 637 | ||
| 638 | // look at bridged methods! | 638 | // look at bridged methods! |
| 639 | MethodEntry bridgedEntry = getBridgedMethod(methodEntry); | 639 | MethodEntry bridgedEntry = getBridgedMethod(methodEntry); |
| 640 | while (bridgedEntry != null) { | 640 | while (bridgedEntry != null) { |
| 641 | methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); | 641 | methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); |
| 642 | bridgedEntry = getBridgedMethod(bridgedEntry); | 642 | bridgedEntry = getBridgedMethod(bridgedEntry); |
| 643 | } | 643 | } |
| 644 | 644 | ||
| 645 | // look at interface methods too | 645 | // look at interface methods too |
| 646 | for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { | 646 | for (MethodImplementationsTreeNode implementationsNode : getMethodImplementations(new Translator(), methodEntry)) { |
| 647 | getRelatedMethodImplementations(methodEntries, implementationsNode); | 647 | getRelatedMethodImplementations(methodEntries, implementationsNode); |
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | // recurse | 650 | // recurse |
| 651 | for (int i = 0; i < node.getChildCount(); i++) { | 651 | for (int i = 0; i < node.getChildCount(); i++) { |
| 652 | getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); | 652 | getRelatedMethodImplementations(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i)); |
| 653 | } | 653 | } |
| 654 | } | 654 | } |
| 655 | 655 | ||
| 656 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { | 656 | private void getRelatedMethodImplementations(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) { |
| 657 | MethodEntry methodEntry = node.getMethodEntry(); | 657 | MethodEntry methodEntry = node.getMethodEntry(); |
| 658 | if (containsObfBehavior(methodEntry)) { | 658 | if (containsObfBehavior(methodEntry)) { |
| 659 | // collect the entry | 659 | // collect the entry |
| 660 | methodEntries.add(methodEntry); | 660 | methodEntries.add(methodEntry); |
| 661 | } | 661 | } |
| 662 | 662 | ||
| 663 | // look at bridged methods! | 663 | // look at bridged methods! |
| 664 | MethodEntry bridgedEntry = getBridgedMethod(methodEntry); | 664 | MethodEntry bridgedEntry = getBridgedMethod(methodEntry); |
| 665 | while (bridgedEntry != null) { | 665 | while (bridgedEntry != null) { |
| 666 | methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); | 666 | methodEntries.addAll(getRelatedMethodImplementations(bridgedEntry)); |
| 667 | bridgedEntry = getBridgedMethod(bridgedEntry); | 667 | bridgedEntry = getBridgedMethod(bridgedEntry); |
| 668 | } | 668 | } |
| 669 | 669 | ||
| 670 | // recurse | 670 | // recurse |
| 671 | for (int i = 0; i < node.getChildCount(); i++) { | 671 | for (int i = 0; i < node.getChildCount(); i++) { |
| 672 | getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); | 672 | getRelatedMethodImplementations(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i)); |
| 673 | } | 673 | } |
| 674 | } | 674 | } |
| 675 | 675 | ||
| 676 | public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { | 676 | public Collection<EntryReference<FieldEntry, BehaviorEntry>> getFieldReferences(FieldEntry fieldEntry) { |
| 677 | return this.fieldReferences.get(fieldEntry); | 677 | return this.fieldReferences.get(fieldEntry); |
| 678 | } | 678 | } |
| 679 | 679 | ||
| 680 | public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { | 680 | public Collection<FieldEntry> getReferencedFields(BehaviorEntry behaviorEntry) { |
| 681 | // linear search is fast enough for now | 681 | // linear search is fast enough for now |
| 682 | Set<FieldEntry> fieldEntries = Sets.newHashSet(); | 682 | Set<FieldEntry> fieldEntries = Sets.newHashSet(); |
| 683 | for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { | 683 | for (EntryReference<FieldEntry, BehaviorEntry> reference : this.fieldReferences.values()) { |
| 684 | if (reference.context == behaviorEntry) { | 684 | if (reference.context == behaviorEntry) { |
| 685 | fieldEntries.add(reference.entry); | 685 | fieldEntries.add(reference.entry); |
| 686 | } | 686 | } |
| 687 | } | 687 | } |
| 688 | return fieldEntries; | 688 | return fieldEntries; |
| 689 | } | 689 | } |
| 690 | 690 | ||
| 691 | public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { | 691 | public Collection<EntryReference<BehaviorEntry, BehaviorEntry>> getBehaviorReferences(BehaviorEntry behaviorEntry) { |
| 692 | return this.behaviorReferences.get(behaviorEntry); | 692 | return this.behaviorReferences.get(behaviorEntry); |
| 693 | } | 693 | } |
| 694 | 694 | ||
| 695 | public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { | 695 | public Collection<BehaviorEntry> getReferencedBehaviors(BehaviorEntry behaviorEntry) { |
| 696 | // linear search is fast enough for now | 696 | // linear search is fast enough for now |
| 697 | Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); | 697 | Set<BehaviorEntry> behaviorEntries = Sets.newHashSet(); |
| 698 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { | 698 | for (EntryReference<BehaviorEntry, BehaviorEntry> reference : this.behaviorReferences.values()) { |
| 699 | if (reference.context == behaviorEntry) { | 699 | if (reference.context == behaviorEntry) { |
| 700 | behaviorEntries.add(reference.entry); | 700 | behaviorEntries.add(reference.entry); |
| 701 | } | 701 | } |
| 702 | } | 702 | } |
| 703 | return behaviorEntries; | 703 | return behaviorEntries; |
| 704 | } | 704 | } |
| 705 | 705 | ||
| 706 | public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { | 706 | public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { |
| 707 | return this.innerClassesByOuter.get(obfOuterClassEntry); | 707 | return this.innerClassesByOuter.get(obfOuterClassEntry); |
| 708 | } | 708 | } |
| 709 | 709 | ||
| 710 | public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { | 710 | public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { |
| 711 | return this.outerClassesByInner.get(obfInnerClassEntry); | 711 | return this.outerClassesByInner.get(obfInnerClassEntry); |
| 712 | } | 712 | } |
| 713 | 713 | ||
| 714 | public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { | 714 | public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { |
| 715 | return this.anonymousClasses.containsKey(obfInnerClassEntry); | 715 | return this.anonymousClasses.containsKey(obfInnerClassEntry); |
| 716 | } | 716 | } |
| 717 | 717 | ||
| 718 | public boolean isSyntheticMethod(MethodEntry methodEntry) { | 718 | public boolean isSyntheticMethod(MethodEntry methodEntry) { |
| 719 | return this.syntheticMethods.contains(methodEntry); | 719 | return this.syntheticMethods.contains(methodEntry); |
| 720 | } | 720 | } |
| 721 | 721 | ||
| 722 | public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { | 722 | public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { |
| 723 | return this.anonymousClasses.get(obfInnerClassName); | 723 | return this.anonymousClasses.get(obfInnerClassName); |
| 724 | } | 724 | } |
| 725 | 725 | ||
| 726 | public Set<ClassEntry> getInterfaces(String className) { | 726 | public Set<ClassEntry> getInterfaces(String className) { |
| 727 | ClassEntry classEntry = new ClassEntry(className); | 727 | ClassEntry classEntry = new ClassEntry(className); |
| 728 | Set<ClassEntry> interfaces = new HashSet<>(); | 728 | Set<ClassEntry> interfaces = new HashSet<>(); |
| 729 | interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); | 729 | interfaces.addAll(this.translationIndex.getInterfaces(classEntry)); |
| 730 | for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { | 730 | for (ClassEntry ancestor : this.translationIndex.getAncestry(classEntry)) { |
| 731 | interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); | 731 | interfaces.addAll(this.translationIndex.getInterfaces(ancestor)); |
| 732 | } | 732 | } |
| 733 | return interfaces; | 733 | return interfaces; |
| 734 | } | 734 | } |
| 735 | 735 | ||
| 736 | public Set<String> getImplementingClasses(String targetInterfaceName) { | 736 | public Set<String> getImplementingClasses(String targetInterfaceName) { |
| 737 | 737 | ||
| 738 | // linear search is fast enough for now | 738 | // linear search is fast enough for now |
| 739 | Set<String> classNames = Sets.newHashSet(); | 739 | Set<String> classNames = Sets.newHashSet(); |
| 740 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) { | 740 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.translationIndex.getClassInterfaces()) { |
| 741 | ClassEntry classEntry = entry.getKey(); | 741 | ClassEntry classEntry = entry.getKey(); |
| 742 | ClassEntry interfaceEntry = entry.getValue(); | 742 | ClassEntry interfaceEntry = entry.getValue(); |
| 743 | if (interfaceEntry.getName().equals(targetInterfaceName)) { | 743 | if (interfaceEntry.getName().equals(targetInterfaceName)) { |
| 744 | String className = classEntry.getClassName(); | 744 | String className = classEntry.getClassName(); |
| 745 | classNames.add(className); | 745 | classNames.add(className); |
| 746 | if (isInterface(className)) { | 746 | if (isInterface(className)) { |
| 747 | classNames.addAll(getImplementingClasses(className)); | 747 | classNames.addAll(getImplementingClasses(className)); |
| 748 | } | 748 | } |
| 749 | 749 | ||
| 750 | this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); | 750 | this.translationIndex.getSubclassNamesRecursively(classNames, classEntry); |
| 751 | } | 751 | } |
| 752 | } | 752 | } |
| 753 | return classNames; | 753 | return classNames; |
| 754 | } | 754 | } |
| 755 | 755 | ||
| 756 | public boolean isInterface(String className) { | 756 | public boolean isInterface(String className) { |
| 757 | return this.translationIndex.isInterface(new ClassEntry(className)); | 757 | return this.translationIndex.isInterface(new ClassEntry(className)); |
| 758 | } | 758 | } |
| 759 | 759 | ||
| 760 | public boolean containsObfClass(ClassEntry obfClassEntry) { | 760 | public boolean containsObfClass(ClassEntry obfClassEntry) { |
| 761 | return this.obfClassEntries.contains(obfClassEntry); | 761 | return this.obfClassEntries.contains(obfClassEntry); |
| 762 | } | 762 | } |
| 763 | 763 | ||
| 764 | public boolean containsObfField(FieldEntry obfFieldEntry) { | 764 | public boolean containsObfField(FieldEntry obfFieldEntry) { |
| 765 | return this.access.containsKey(obfFieldEntry); | 765 | return this.access.containsKey(obfFieldEntry); |
| 766 | } | 766 | } |
| 767 | 767 | ||
| 768 | public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { | 768 | public boolean containsObfBehavior(BehaviorEntry obfBehaviorEntry) { |
| 769 | return this.access.containsKey(obfBehaviorEntry); | 769 | return this.access.containsKey(obfBehaviorEntry); |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | public boolean containsEntryWithSameName(Entry entry) | 772 | public boolean containsEntryWithSameName(Entry entry) { |
| 773 | { | 773 | for (Entry target : this.access.keySet()) |
| 774 | for (Entry target : this.access.keySet()) | 774 | if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) |
| 775 | if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass())) | 775 | return true; |
| 776 | return true; | 776 | return false; |
| 777 | return false; | 777 | } |
| 778 | } | 778 | |
| 779 | 779 | public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { | |
| 780 | public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { | 780 | // check the behavior |
| 781 | // check the behavior | 781 | if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { |
| 782 | if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { | 782 | return false; |
| 783 | return false; | 783 | } |
| 784 | } | 784 | |
| 785 | 785 | // check the argument | |
| 786 | // check the argument | 786 | return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); |
| 787 | return obfArgumentEntry.getIndex() < obfArgumentEntry.getBehaviorEntry().getSignature().getArgumentTypes().size(); | 787 | |
| 788 | 788 | } | |
| 789 | } | 789 | |
| 790 | 790 | public boolean containsObfEntry(Entry obfEntry) { | |
| 791 | public boolean containsObfEntry(Entry obfEntry) { | 791 | if (obfEntry instanceof ClassEntry) { |
| 792 | if (obfEntry instanceof ClassEntry) { | 792 | return containsObfClass((ClassEntry) obfEntry); |
| 793 | return containsObfClass((ClassEntry) obfEntry); | 793 | } else if (obfEntry instanceof FieldEntry) { |
| 794 | } else if (obfEntry instanceof FieldEntry) { | 794 | return containsObfField((FieldEntry) obfEntry); |
| 795 | return containsObfField((FieldEntry) obfEntry); | 795 | } else if (obfEntry instanceof BehaviorEntry) { |
| 796 | } else if (obfEntry instanceof BehaviorEntry) { | 796 | return containsObfBehavior((BehaviorEntry) obfEntry); |
| 797 | return containsObfBehavior((BehaviorEntry) obfEntry); | 797 | } else if (obfEntry instanceof ArgumentEntry) { |
| 798 | } else if (obfEntry instanceof ArgumentEntry) { | 798 | return containsObfArgument((ArgumentEntry) obfEntry); |
| 799 | return containsObfArgument((ArgumentEntry) obfEntry); | 799 | } else if (obfEntry instanceof LocalVariableEntry) { |
| 800 | } else if (obfEntry instanceof LocalVariableEntry) { | 800 | // TODO: Implement it |
| 801 | // TODO: Implement it | 801 | return false; |
| 802 | return false; | 802 | } else { |
| 803 | } else { | 803 | throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); |
| 804 | throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); | 804 | } |
| 805 | } | 805 | } |
| 806 | } | 806 | |
| 807 | 807 | public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { | |
| 808 | public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { | 808 | return this.bridgedMethods.get(bridgeMethodEntry); |
| 809 | return this.bridgedMethods.get(bridgeMethodEntry); | 809 | } |
| 810 | } | 810 | |
| 811 | 811 | public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { | |
| 812 | public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { | 812 | |
| 813 | 813 | // build class chain in inner-to-outer order | |
| 814 | // build class chain in inner-to-outer order | 814 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); |
| 815 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); | 815 | ClassEntry checkClassEntry = obfClassEntry; |
| 816 | ClassEntry checkClassEntry = obfClassEntry; | 816 | while (true) { |
| 817 | while (true) { | 817 | ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); |
| 818 | ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); | 818 | if (obfOuterClassEntry != null) { |
| 819 | if (obfOuterClassEntry != null) { | 819 | obfClassChain.add(obfOuterClassEntry); |
| 820 | obfClassChain.add(obfOuterClassEntry); | 820 | checkClassEntry = obfOuterClassEntry; |
| 821 | checkClassEntry = obfOuterClassEntry; | 821 | } else { |
| 822 | } else { | 822 | break; |
| 823 | break; | 823 | } |
| 824 | } | 824 | } |
| 825 | } | 825 | |
| 826 | 826 | // switch to outer-to-inner order | |
| 827 | // switch to outer-to-inner order | 827 | Collections.reverse(obfClassChain); |
| 828 | Collections.reverse(obfClassChain); | 828 | |
| 829 | 829 | return obfClassChain; | |
| 830 | return obfClassChain; | 830 | } |
| 831 | } | ||
| 832 | } | 831 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 9bd6219a..bacb1aac 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java | |||
| @@ -8,87 +8,86 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | |||
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 18 | |||
| 19 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 20 | import cuchaz.enigma.mapping.MethodEntry; | 16 | import cuchaz.enigma.mapping.MethodEntry; |
| 21 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.mapping.Translator; |
| 22 | 18 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 23 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { | 22 | public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { |
| 24 | 23 | ||
| 25 | private Translator deobfuscatingTranslator; | 24 | private Translator deobfuscatingTranslator; |
| 26 | private MethodEntry entry; | 25 | private MethodEntry entry; |
| 27 | 26 | ||
| 28 | public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { | 27 | public MethodImplementationsTreeNode(Translator deobfuscatingTranslator, MethodEntry entry) { |
| 29 | if (entry == null) { | 28 | if (entry == null) { |
| 30 | throw new IllegalArgumentException("Entry cannot be null!"); | 29 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 32 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 34 | this.entry = entry; | 33 | this.entry = entry; |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | public MethodEntry getMethodEntry() { | 36 | public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { |
| 38 | return this.entry; | 37 | // is this the node? |
| 39 | } | 38 | if (node.getMethodEntry().equals(entry)) { |
| 40 | 39 | return node; | |
| 41 | public String getDeobfClassName() { | 40 | } |
| 42 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); | 41 | |
| 43 | } | 42 | // recurse |
| 44 | 43 | for (int i = 0; i < node.getChildCount(); i++) { | |
| 45 | public String getDeobfMethodName() { | 44 | MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); |
| 46 | return this.deobfuscatingTranslator.translate(this.entry); | 45 | if (foundNode != null) { |
| 47 | } | 46 | return foundNode; |
| 48 | 47 | } | |
| 49 | @Override | 48 | } |
| 50 | public String toString() { | 49 | return null; |
| 51 | String className = getDeobfClassName(); | 50 | } |
| 52 | if (className == null) { | 51 | |
| 53 | className = this.entry.getClassName(); | 52 | public MethodEntry getMethodEntry() { |
| 54 | } | 53 | return this.entry; |
| 55 | 54 | } | |
| 56 | String methodName = getDeobfMethodName(); | 55 | |
| 57 | if (methodName == null) { | 56 | public String getDeobfClassName() { |
| 58 | methodName = this.entry.getName(); | 57 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); |
| 59 | } | 58 | } |
| 60 | return className + "." + methodName + "()"; | 59 | |
| 61 | } | 60 | public String getDeobfMethodName() { |
| 62 | 61 | return this.deobfuscatingTranslator.translate(this.entry); | |
| 63 | public void load(JarIndex index) { | 62 | } |
| 64 | 63 | ||
| 65 | // get all method implementations | 64 | @Override |
| 66 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); | 65 | public String toString() { |
| 67 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { | 66 | String className = getDeobfClassName(); |
| 68 | MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() | 67 | if (className == null) { |
| 69 | ); | 68 | className = this.entry.getClassName(); |
| 70 | if (index.containsObfBehavior(methodEntry)) { | 69 | } |
| 71 | nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); | 70 | |
| 72 | } | 71 | String methodName = getDeobfMethodName(); |
| 73 | } | 72 | if (methodName == null) { |
| 74 | 73 | methodName = this.entry.getName(); | |
| 75 | // add them to this node | 74 | } |
| 76 | nodes.forEach(this::add); | 75 | return className + "." + methodName + "()"; |
| 77 | } | 76 | } |
| 78 | 77 | ||
| 79 | public static MethodImplementationsTreeNode findNode(MethodImplementationsTreeNode node, MethodEntry entry) { | 78 | public void load(JarIndex index) { |
| 80 | // is this the node? | 79 | |
| 81 | if (node.getMethodEntry().equals(entry)) { | 80 | // get all method implementations |
| 82 | return node; | 81 | List<MethodImplementationsTreeNode> nodes = Lists.newArrayList(); |
| 83 | } | 82 | for (String implementingClassName : index.getImplementingClasses(this.entry.getClassName())) { |
| 84 | 83 | MethodEntry methodEntry = new MethodEntry(new ClassEntry(implementingClassName), this.entry.getName(), this.entry.getSignature() | |
| 85 | // recurse | 84 | ); |
| 86 | for (int i = 0; i < node.getChildCount(); i++) { | 85 | if (index.containsObfBehavior(methodEntry)) { |
| 87 | MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); | 86 | nodes.add(new MethodImplementationsTreeNode(this.deobfuscatingTranslator, methodEntry)); |
| 88 | if (foundNode != null) { | 87 | } |
| 89 | return foundNode; | 88 | } |
| 90 | } | 89 | |
| 91 | } | 90 | // add them to this node |
| 92 | return null; | 91 | nodes.forEach(this::add); |
| 93 | } | 92 | } |
| 94 | } | 93 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index b65b8c10..4f84dd09 100644 --- a/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java | |||
| @@ -8,97 +8,96 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | |||
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 18 | |||
| 19 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 20 | import cuchaz.enigma.mapping.MethodEntry; | 16 | import cuchaz.enigma.mapping.MethodEntry; |
| 21 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.mapping.Translator; |
| 22 | 18 | ||
| 19 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 23 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { | 22 | public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { |
| 24 | 23 | ||
| 25 | private Translator deobfuscatingTranslator; | 24 | private Translator deobfuscatingTranslator; |
| 26 | private MethodEntry entry; | 25 | private MethodEntry entry; |
| 27 | private boolean isImplemented; | 26 | private boolean isImplemented; |
| 28 | 27 | ||
| 29 | public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { | 28 | public MethodInheritanceTreeNode(Translator deobfuscatingTranslator, MethodEntry entry, boolean isImplemented) { |
| 30 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 29 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 31 | this.entry = entry; | 30 | this.entry = entry; |
| 32 | this.isImplemented = isImplemented; | 31 | this.isImplemented = isImplemented; |
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | public MethodEntry getMethodEntry() { | 34 | public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { |
| 36 | return this.entry; | 35 | // is this the node? |
| 37 | } | 36 | if (node.getMethodEntry().equals(entry)) { |
| 38 | 37 | return node; | |
| 39 | public String getDeobfClassName() { | 38 | } |
| 40 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); | 39 | |
| 41 | } | 40 | // recurse |
| 42 | 41 | for (int i = 0; i < node.getChildCount(); i++) { | |
| 43 | public String getDeobfMethodName() { | 42 | MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); |
| 44 | return this.deobfuscatingTranslator.translate(this.entry); | 43 | if (foundNode != null) { |
| 45 | } | 44 | return foundNode; |
| 46 | 45 | } | |
| 47 | public boolean isImplemented() { | 46 | } |
| 48 | return this.isImplemented; | 47 | return null; |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | @Override | 50 | public MethodEntry getMethodEntry() { |
| 52 | public String toString() { | 51 | return this.entry; |
| 53 | String className = getDeobfClassName(); | 52 | } |
| 54 | if (className == null) { | 53 | |
| 55 | className = this.entry.getClassName(); | 54 | public String getDeobfClassName() { |
| 56 | } | 55 | return this.deobfuscatingTranslator.translateClass(this.entry.getClassName()); |
| 57 | 56 | } | |
| 58 | if (!this.isImplemented) { | 57 | |
| 59 | return className; | 58 | public String getDeobfMethodName() { |
| 60 | } else { | 59 | return this.deobfuscatingTranslator.translate(this.entry); |
| 61 | String methodName = getDeobfMethodName(); | 60 | } |
| 62 | if (methodName == null) { | 61 | |
| 63 | methodName = this.entry.getName(); | 62 | public boolean isImplemented() { |
| 64 | } | 63 | return this.isImplemented; |
| 65 | return className + "." + methodName + "()"; | 64 | } |
| 66 | } | 65 | |
| 67 | } | 66 | @Override |
| 68 | 67 | public String toString() { | |
| 69 | public void load(JarIndex index, boolean recurse) { | 68 | String className = getDeobfClassName(); |
| 70 | // get all the child nodes | 69 | if (className == null) { |
| 71 | List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); | 70 | className = this.entry.getClassName(); |
| 72 | for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { | 71 | } |
| 73 | MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() | 72 | |
| 74 | ); | 73 | if (!this.isImplemented) { |
| 75 | nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) | 74 | return className; |
| 76 | )); | 75 | } else { |
| 77 | } | 76 | String methodName = getDeobfMethodName(); |
| 78 | 77 | if (methodName == null) { | |
| 79 | // add them to this node | 78 | methodName = this.entry.getName(); |
| 80 | nodes.forEach(this::add); | 79 | } |
| 81 | 80 | return className + "." + methodName + "()"; | |
| 82 | if (recurse) { | 81 | } |
| 83 | for (MethodInheritanceTreeNode node : nodes) { | 82 | } |
| 84 | node.load(index, true); | 83 | |
| 85 | } | 84 | public void load(JarIndex index, boolean recurse) { |
| 86 | } | 85 | // get all the child nodes |
| 87 | } | 86 | List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); |
| 88 | 87 | for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(this.entry.getClassEntry())) { | |
| 89 | public static MethodInheritanceTreeNode findNode(MethodInheritanceTreeNode node, MethodEntry entry) { | 88 | MethodEntry methodEntry = new MethodEntry(subclassEntry, this.entry.getName(), this.entry.getSignature() |
| 90 | // is this the node? | 89 | ); |
| 91 | if (node.getMethodEntry().equals(entry)) { | 90 | nodes.add(new MethodInheritanceTreeNode(this.deobfuscatingTranslator, methodEntry, index.containsObfBehavior(methodEntry) |
| 92 | return node; | 91 | )); |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | // recurse | 94 | // add them to this node |
| 96 | for (int i = 0; i < node.getChildCount(); i++) { | 95 | nodes.forEach(this::add); |
| 97 | MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); | 96 | |
| 98 | if (foundNode != null) { | 97 | if (recurse) { |
| 99 | return foundNode; | 98 | for (MethodInheritanceTreeNode node : nodes) { |
| 100 | } | 99 | node.load(index, true); |
| 101 | } | 100 | } |
| 102 | return null; | 101 | } |
| 103 | } | 102 | } |
| 104 | } | 103 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java index 93923467..04693637 100644 --- a/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java | |||
| @@ -8,12 +8,13 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.Entry; | 14 | import cuchaz.enigma.mapping.Entry; |
| 14 | 15 | ||
| 15 | public interface ReferenceTreeNode<E extends Entry, C extends Entry> { | 16 | public interface ReferenceTreeNode<E extends Entry, C extends Entry> { |
| 16 | E getEntry(); | 17 | E getEntry(); |
| 17 | 18 | ||
| 18 | EntryReference<E, C> getReference(); | 19 | EntryReference<E, C> getReference(); |
| 19 | } | 20 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index 719930e9..19250c8d 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java | |||
| @@ -8,174 +8,173 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 14 | import com.google.common.collect.Lists; | 15 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 17 | |||
| 18 | import com.strobel.decompiler.languages.Region; | 18 | import com.strobel.decompiler.languages.Region; |
| 19 | import com.strobel.decompiler.languages.java.ast.AstNode; | 19 | import com.strobel.decompiler.languages.java.ast.AstNode; |
| 20 | import com.strobel.decompiler.languages.java.ast.Identifier; | 20 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 21 | import cuchaz.enigma.mapping.Entry; | ||
| 21 | 22 | ||
| 22 | import java.util.Collection; | 23 | import java.util.Collection; |
| 23 | import java.util.List; | 24 | import java.util.List; |
| 24 | import java.util.Map; | 25 | import java.util.Map; |
| 25 | import java.util.TreeMap; | 26 | import java.util.TreeMap; |
| 26 | 27 | ||
| 27 | import cuchaz.enigma.mapping.Entry; | ||
| 28 | |||
| 29 | public class SourceIndex { | 28 | public class SourceIndex { |
| 30 | 29 | ||
| 31 | private String source; | 30 | private String source; |
| 32 | private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; | 31 | private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; |
| 33 | private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; | 32 | private Multimap<EntryReference<Entry, Entry>, Token> referenceToTokens; |
| 34 | private Map<Entry, Token> declarationToToken; | 33 | private Map<Entry, Token> declarationToToken; |
| 35 | private List<Integer> lineOffsets; | 34 | private List<Integer> lineOffsets; |
| 36 | private boolean ignoreBadTokens; | 35 | private boolean ignoreBadTokens; |
| 37 | 36 | ||
| 38 | public SourceIndex(String source) { | 37 | public SourceIndex(String source) { |
| 39 | this(source, true); | 38 | this(source, true); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | public SourceIndex(String source, boolean ignoreBadTokens) { | 41 | public SourceIndex(String source, boolean ignoreBadTokens) { |
| 43 | this.source = source; | 42 | this.source = source; |
| 44 | this.ignoreBadTokens = ignoreBadTokens; | 43 | this.ignoreBadTokens = ignoreBadTokens; |
| 45 | this.tokenToReference = Maps.newTreeMap(); | 44 | this.tokenToReference = Maps.newTreeMap(); |
| 46 | this.referenceToTokens = HashMultimap.create(); | 45 | this.referenceToTokens = HashMultimap.create(); |
| 47 | this.declarationToToken = Maps.newHashMap(); | 46 | this.declarationToToken = Maps.newHashMap(); |
| 48 | this.lineOffsets = Lists.newArrayList(); | 47 | this.lineOffsets = Lists.newArrayList(); |
| 49 | 48 | ||
| 50 | // count the lines | 49 | // count the lines |
| 51 | this.lineOffsets.add(0); | 50 | this.lineOffsets.add(0); |
| 52 | for (int i = 0; i < source.length(); i++) { | 51 | for (int i = 0; i < source.length(); i++) { |
| 53 | if (source.charAt(i) == '\n') { | 52 | if (source.charAt(i) == '\n') { |
| 54 | this.lineOffsets.add(i + 1); | 53 | this.lineOffsets.add(i + 1); |
| 55 | } | 54 | } |
| 56 | } | 55 | } |
| 57 | } | 56 | } |
| 58 | 57 | ||
| 59 | public String getSource() { | 58 | public String getSource() { |
| 60 | return this.source; | 59 | return this.source; |
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | public Token getToken(AstNode node) { | 62 | public Token getToken(AstNode node) { |
| 64 | 63 | ||
| 65 | // get the text of the node | 64 | // get the text of the node |
| 66 | String name = ""; | 65 | String name = ""; |
| 67 | if (node instanceof Identifier) { | 66 | if (node instanceof Identifier) { |
| 68 | name = ((Identifier) node).getName(); | 67 | name = ((Identifier) node).getName(); |
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | // get a token for this node's region | 70 | // get a token for this node's region |
| 72 | Region region = node.getRegion(); | 71 | Region region = node.getRegion(); |
| 73 | if (region.getBeginLine() == 0 || region.getEndLine() == 0) { | 72 | if (region.getBeginLine() == 0 || region.getEndLine() == 0) { |
| 74 | // DEBUG | 73 | // DEBUG |
| 75 | System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); | 74 | System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region)); |
| 76 | return null; | 75 | return null; |
| 77 | } | 76 | } |
| 78 | Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); | 77 | Token token = new Token(toPos(region.getBeginLine(), region.getBeginColumn()), toPos(region.getEndLine(), region.getEndColumn()), this.source); |
| 79 | if (token.start == 0) { | 78 | if (token.start == 0) { |
| 80 | // DEBUG | 79 | // DEBUG |
| 81 | System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); | 80 | System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region)); |
| 82 | return null; | 81 | return null; |
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | // DEBUG | 84 | // DEBUG |
| 86 | // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); | 85 | // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); |
| 87 | 86 | ||
| 88 | // if the token has a $ in it, something's wrong. Ignore this token | 87 | // if the token has a $ in it, something's wrong. Ignore this token |
| 89 | if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { | 88 | if (name.lastIndexOf('$') >= 0 && this.ignoreBadTokens) { |
| 90 | // DEBUG | 89 | // DEBUG |
| 91 | System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); | 90 | System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); |
| 92 | return null; | 91 | return null; |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | return token; | 94 | return token; |
| 96 | } | 95 | } |
| 97 | 96 | ||
| 98 | public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { | 97 | public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) { |
| 99 | Token token = getToken(node); | 98 | Token token = getToken(node); |
| 100 | if (token != null) { | 99 | if (token != null) { |
| 101 | EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); | 100 | EntryReference<Entry, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); |
| 102 | this.tokenToReference.put(token, deobfReference); | 101 | this.tokenToReference.put(token, deobfReference); |
| 103 | this.referenceToTokens.put(deobfReference, token); | 102 | this.referenceToTokens.put(deobfReference, token); |
| 104 | } | 103 | } |
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | public void addDeclaration(AstNode node, Entry deobfEntry) { | 106 | public void addDeclaration(AstNode node, Entry deobfEntry) { |
| 108 | Token token = getToken(node); | 107 | Token token = getToken(node); |
| 109 | if (token != null) { | 108 | if (token != null) { |
| 110 | EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); | 109 | EntryReference<Entry, Entry> reference = new EntryReference<>(deobfEntry, token.text); |
| 111 | this.tokenToReference.put(token, reference); | 110 | this.tokenToReference.put(token, reference); |
| 112 | this.referenceToTokens.put(reference, token); | 111 | this.referenceToTokens.put(reference, token); |
| 113 | this.declarationToToken.put(deobfEntry, token); | 112 | this.declarationToToken.put(deobfEntry, token); |
| 114 | } | 113 | } |
| 115 | } | 114 | } |
| 116 | 115 | ||
| 117 | public Token getReferenceToken(int pos) { | 116 | public Token getReferenceToken(int pos) { |
| 118 | Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); | 117 | Token token = this.tokenToReference.floorKey(new Token(pos, pos, null)); |
| 119 | if (token != null && token.contains(pos)) { | 118 | if (token != null && token.contains(pos)) { |
| 120 | return token; | 119 | return token; |
| 121 | } | 120 | } |
| 122 | return null; | 121 | return null; |
| 123 | } | 122 | } |
| 124 | 123 | ||
| 125 | public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { | 124 | public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) { |
| 126 | return this.referenceToTokens.get(deobfReference); | 125 | return this.referenceToTokens.get(deobfReference); |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | 128 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { |
| 130 | if (token == null) { | 129 | if (token == null) { |
| 131 | return null; | 130 | return null; |
| 132 | } | 131 | } |
| 133 | return this.tokenToReference.get(token); | 132 | return this.tokenToReference.get(token); |
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { | 135 | public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) { |
| 137 | EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); | 136 | EntryReference<Entry, Entry> oldDeobfReference = this.tokenToReference.get(token); |
| 138 | this.tokenToReference.put(token, newDeobfReference); | 137 | this.tokenToReference.put(token, newDeobfReference); |
| 139 | Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); | 138 | Collection<Token> tokens = this.referenceToTokens.get(oldDeobfReference); |
| 140 | this.referenceToTokens.removeAll(oldDeobfReference); | 139 | this.referenceToTokens.removeAll(oldDeobfReference); |
| 141 | this.referenceToTokens.putAll(newDeobfReference, tokens); | 140 | this.referenceToTokens.putAll(newDeobfReference, tokens); |
| 142 | } | 141 | } |
| 143 | 142 | ||
| 144 | public Iterable<Token> referenceTokens() { | 143 | public Iterable<Token> referenceTokens() { |
| 145 | return this.tokenToReference.keySet(); | 144 | return this.tokenToReference.keySet(); |
| 146 | } | 145 | } |
| 147 | 146 | ||
| 148 | public Iterable<Token> declarationTokens() { | 147 | public Iterable<Token> declarationTokens() { |
| 149 | return this.declarationToToken.values(); | 148 | return this.declarationToToken.values(); |
| 150 | } | 149 | } |
| 151 | 150 | ||
| 152 | public Iterable<Entry> declarations() { | 151 | public Iterable<Entry> declarations() { |
| 153 | return this.declarationToToken.keySet(); | 152 | return this.declarationToToken.keySet(); |
| 154 | } | 153 | } |
| 155 | 154 | ||
| 156 | public Token getDeclarationToken(Entry deobfEntry) { | 155 | public Token getDeclarationToken(Entry deobfEntry) { |
| 157 | return this.declarationToToken.get(deobfEntry); | 156 | return this.declarationToToken.get(deobfEntry); |
| 158 | } | 157 | } |
| 159 | 158 | ||
| 160 | public int getLineNumber(int pos) { | 159 | public int getLineNumber(int pos) { |
| 161 | // line number is 1-based | 160 | // line number is 1-based |
| 162 | int line = 0; | 161 | int line = 0; |
| 163 | for (Integer offset : this.lineOffsets) { | 162 | for (Integer offset : this.lineOffsets) { |
| 164 | if (offset > pos) { | 163 | if (offset > pos) { |
| 165 | break; | 164 | break; |
| 166 | } | 165 | } |
| 167 | line++; | 166 | line++; |
| 168 | } | 167 | } |
| 169 | return line; | 168 | return line; |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | public int getColumnNumber(int pos) { | 171 | public int getColumnNumber(int pos) { |
| 173 | // column number is 1-based | 172 | // column number is 1-based |
| 174 | return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; | 173 | return pos - this.lineOffsets.get(getLineNumber(pos) - 1) + 1; |
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | private int toPos(int line, int col) { | 176 | private int toPos(int line, int col) { |
| 178 | // line and col are 1-based | 177 | // line and col are 1-based |
| 179 | return this.lineOffsets.get(line - 1) + col - 1; | 178 | return this.lineOffsets.get(line - 1) + col - 1; |
| 180 | } | 179 | } |
| 181 | } | 180 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java index bfd5a562..4febf256 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| @@ -26,186 +27,179 @@ import java.util.Map; | |||
| 26 | 27 | ||
| 27 | public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { | 28 | public class SourceIndexBehaviorVisitor extends SourceIndexVisitor { |
| 28 | 29 | ||
| 29 | private BehaviorEntry behaviorEntry; | 30 | private BehaviorEntry behaviorEntry; |
| 30 | 31 | ||
| 31 | // TODO: Really fix Procyon index problem with inner classes | 32 | // TODO: Really fix Procyon index problem with inner classes |
| 32 | private int argumentPosition; | 33 | private int argumentPosition; |
| 33 | private int localsPosition; | 34 | private int localsPosition; |
| 34 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); | 35 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); |
| 35 | private Map<String, Entry> identifierEntryCache = new HashMap<>(); | 36 | private Map<String, Entry> identifierEntryCache = new HashMap<>(); |
| 36 | 37 | ||
| 37 | public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { | 38 | public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { |
| 38 | this.behaviorEntry = behaviorEntry; | 39 | this.behaviorEntry = behaviorEntry; |
| 39 | this.argumentPosition = 0; | 40 | this.argumentPosition = 0; |
| 40 | this.localsPosition = 0; | 41 | this.localsPosition = 0; |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | @Override | 44 | @Override |
| 44 | public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { | 45 | public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { |
| 45 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 46 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 46 | 47 | ||
| 47 | // get the behavior entry | 48 | // get the behavior entry |
| 48 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); | 49 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 49 | BehaviorEntry behaviorEntry = null; | 50 | BehaviorEntry behaviorEntry = null; |
| 50 | if (ref instanceof MethodReference) { | 51 | if (ref instanceof MethodReference) { |
| 51 | MethodReference methodRef = (MethodReference) ref; | 52 | MethodReference methodRef = (MethodReference) ref; |
| 52 | if (methodRef.isConstructor()) { | 53 | if (methodRef.isConstructor()) { |
| 53 | behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); | 54 | behaviorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); |
| 54 | } else if (methodRef.isTypeInitializer()) { | 55 | } else if (methodRef.isTypeInitializer()) { |
| 55 | behaviorEntry = new ConstructorEntry(classEntry); | 56 | behaviorEntry = new ConstructorEntry(classEntry); |
| 56 | } else { | 57 | } else { |
| 57 | behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); | 58 | behaviorEntry = new MethodEntry(classEntry, ref.getName(), new Signature(ref.getErasedSignature())); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | if (behaviorEntry != null) { | 61 | if (behaviorEntry != null) { |
| 61 | // get the node for the token | 62 | // get the node for the token |
| 62 | AstNode tokenNode = null; | 63 | AstNode tokenNode = null; |
| 63 | if (node.getTarget() instanceof MemberReferenceExpression) { | 64 | if (node.getTarget() instanceof MemberReferenceExpression) { |
| 64 | tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); | 65 | tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); |
| 65 | } else if (node.getTarget() instanceof SuperReferenceExpression) { | 66 | } else if (node.getTarget() instanceof SuperReferenceExpression) { |
| 66 | tokenNode = node.getTarget(); | 67 | tokenNode = node.getTarget(); |
| 67 | } else if (node.getTarget() instanceof ThisReferenceExpression) { | 68 | } else if (node.getTarget() instanceof ThisReferenceExpression) { |
| 68 | tokenNode = node.getTarget(); | 69 | tokenNode = node.getTarget(); |
| 69 | } | 70 | } |
| 70 | if (tokenNode != null) { | 71 | if (tokenNode != null) { |
| 71 | index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); | 72 | index.addReference(tokenNode, behaviorEntry, this.behaviorEntry); |
| 72 | } | 73 | } |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | // Check for identifier | 76 | // Check for identifier |
| 76 | node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) | 77 | node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) |
| 77 | .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); | 78 | .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); |
| 78 | return recurse(node, index); | 79 | return recurse(node, index); |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | @Override | 82 | @Override |
| 82 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { | 83 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { |
| 83 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 84 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 84 | if (ref != null) { | 85 | if (ref != null) { |
| 85 | // make sure this is actually a field | 86 | // make sure this is actually a field |
| 86 | if (ref.getErasedSignature().indexOf('(') >= 0) { | 87 | if (ref.getErasedSignature().indexOf('(') >= 0) { |
| 87 | throw new Error("Expected a field here! got " + ref); | 88 | throw new Error("Expected a field here! got " + ref); |
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); | 91 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 91 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); | 92 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); |
| 92 | index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); | 93 | index.addReference(node.getMemberNameToken(), fieldEntry, this.behaviorEntry); |
| 93 | } | 94 | } |
| 94 | 95 | ||
| 95 | return recurse(node, index); | 96 | return recurse(node, index); |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | @Override | 99 | @Override |
| 99 | public Void visitSimpleType(SimpleType node, SourceIndex index) { | 100 | public Void visitSimpleType(SimpleType node, SourceIndex index) { |
| 100 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); | 101 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); |
| 101 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { | 102 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { |
| 102 | ClassEntry classEntry = new ClassEntry(ref.getInternalName()); | 103 | ClassEntry classEntry = new ClassEntry(ref.getInternalName()); |
| 103 | index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); | 104 | index.addReference(node.getIdentifierToken(), classEntry, this.behaviorEntry); |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | return recurse(node, index); | 107 | return recurse(node, index); |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | @Override | 110 | @Override |
| 110 | public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { | 111 | public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { |
| 111 | ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); | 112 | ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); |
| 112 | if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) | 113 | if (def.getMethod() instanceof MemberReference && def.getMethod() instanceof MethodReference) { |
| 113 | { | 114 | ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), |
| 114 | ArgumentEntry argumentEntry = new ArgumentEntry(ProcyonEntryFactory.getBehaviorEntry((MethodReference) def.getMethod()), | 115 | argumentPosition++, node.getName()); |
| 115 | argumentPosition++, node.getName()); | 116 | Identifier identifier = node.getNameToken(); |
| 116 | Identifier identifier = node.getNameToken(); | 117 | // cache the argument entry and the identifier |
| 117 | // cache the argument entry and the identifier | 118 | identifierEntryCache.put(identifier.getName(), argumentEntry); |
| 118 | identifierEntryCache.put(identifier.getName(), argumentEntry); | 119 | index.addDeclaration(identifier, argumentEntry); |
| 119 | index.addDeclaration(identifier, argumentEntry); | 120 | } |
| 120 | } | 121 | |
| 121 | 122 | return recurse(node, index); | |
| 122 | return recurse(node, index); | 123 | } |
| 123 | } | 124 | |
| 124 | 125 | @Override | |
| 125 | @Override | 126 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { |
| 126 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { | 127 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 127 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 128 | if (ref != null) { |
| 128 | if (ref != null) { | 129 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 129 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); | 130 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); |
| 130 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); | 131 | index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); |
| 131 | index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); | 132 | } else |
| 132 | } | 133 | this.checkIdentifier(node, index); |
| 133 | else | 134 | return recurse(node, index); |
| 134 | this.checkIdentifier(node, index); | 135 | } |
| 135 | return recurse(node, index); | 136 | |
| 136 | } | 137 | private void checkIdentifier(IdentifierExpression node, SourceIndex index) { |
| 137 | 138 | if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! | |
| 138 | private void checkIdentifier(IdentifierExpression node, SourceIndex index) | 139 | index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); |
| 139 | { | 140 | else |
| 140 | if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! | 141 | unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! |
| 141 | index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier())); | 142 | } |
| 142 | else | 143 | |
| 143 | unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! | 144 | private void addDeclarationToUnmatched(String key, SourceIndex index) { |
| 144 | } | 145 | Entry entry = identifierEntryCache.get(key); |
| 145 | 146 | ||
| 146 | private void addDeclarationToUnmatched(String key, SourceIndex index) | 147 | // This cannot happened in theory |
| 147 | { | 148 | if (entry == null) |
| 148 | Entry entry = identifierEntryCache.get(key); | 149 | return; |
| 149 | 150 | for (Identifier identifier : unmatchedIdentifier.get(key)) | |
| 150 | // This cannot happened in theory | 151 | index.addDeclaration(identifier, entry); |
| 151 | if (entry == null) | 152 | unmatchedIdentifier.removeAll(key); |
| 152 | return; | 153 | } |
| 153 | for (Identifier identifier : unmatchedIdentifier.get(key)) | 154 | |
| 154 | index.addDeclaration(identifier, entry); | 155 | @Override |
| 155 | unmatchedIdentifier.removeAll(key); | 156 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { |
| 156 | } | 157 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 157 | 158 | if (ref != null) { | |
| 158 | @Override | 159 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 159 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { | 160 | ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); |
| 160 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 161 | if (node.getType() instanceof SimpleType) { |
| 161 | if (ref != null) { | 162 | SimpleType simpleTypeNode = (SimpleType) node.getType(); |
| 162 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); | 163 | index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); |
| 163 | ConstructorEntry constructorEntry = new ConstructorEntry(classEntry, new Signature(ref.getErasedSignature())); | 164 | } |
| 164 | if (node.getType() instanceof SimpleType) { | 165 | } |
| 165 | SimpleType simpleTypeNode = (SimpleType) node.getType(); | 166 | |
| 166 | index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.behaviorEntry); | 167 | return recurse(node, index); |
| 167 | } | 168 | } |
| 168 | } | 169 | |
| 169 | 170 | @Override | |
| 170 | return recurse(node, index); | 171 | public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { |
| 171 | } | 172 | if (node.getVariableType() instanceof SimpleType) { |
| 172 | 173 | SimpleType type = (SimpleType) node.getVariableType(); | |
| 173 | @Override | 174 | TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); |
| 174 | public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { | 175 | Identifier identifier = node.getVariableNameToken(); |
| 175 | if (node.getVariableType() instanceof SimpleType) | 176 | String signature = Descriptor.of(typeReference.getErasedDescription()); |
| 176 | { | 177 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); |
| 177 | SimpleType type = (SimpleType) node.getVariableType(); | 178 | identifierEntryCache.put(identifier.getName(), localVariableEntry); |
| 178 | TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); | 179 | addDeclarationToUnmatched(identifier.getName(), index); |
| 179 | Identifier identifier = node.getVariableNameToken(); | 180 | index.addDeclaration(identifier, localVariableEntry); |
| 180 | String signature = Descriptor.of(typeReference.getErasedDescription()); | 181 | } |
| 181 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature)); | 182 | return recurse(node, index); |
| 182 | identifierEntryCache.put(identifier.getName(), localVariableEntry); | 183 | } |
| 183 | addDeclarationToUnmatched(identifier.getName(), index); | 184 | |
| 184 | index.addDeclaration(identifier, localVariableEntry); | 185 | @Override |
| 185 | } | 186 | public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { |
| 186 | return recurse(node, index); | 187 | AstNodeCollection<VariableInitializer> variables = node.getVariables(); |
| 187 | } | 188 | |
| 188 | 189 | // Single assignation | |
| 189 | @Override | 190 | if (variables.size() == 1) { |
| 190 | public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { | 191 | VariableInitializer initializer = variables.firstOrNullObject(); |
| 191 | AstNodeCollection<VariableInitializer> variables = node.getVariables(); | 192 | if (initializer != null && node.getType() instanceof SimpleType) { |
| 192 | 193 | SimpleType type = (SimpleType) node.getType(); | |
| 193 | // Single assignation | 194 | TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); |
| 194 | if (variables.size() == 1) | 195 | String signature = Descriptor.of(typeReference.getErasedDescription()); |
| 195 | { | 196 | Identifier identifier = initializer.getNameToken(); |
| 196 | VariableInitializer initializer = variables.firstOrNullObject(); | 197 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); |
| 197 | if (initializer != null && node.getType() instanceof SimpleType) | 198 | identifierEntryCache.put(identifier.getName(), localVariableEntry); |
| 198 | { | 199 | addDeclarationToUnmatched(identifier.getName(), index); |
| 199 | SimpleType type = (SimpleType) node.getType(); | 200 | index.addDeclaration(identifier, localVariableEntry); |
| 200 | TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE); | 201 | } |
| 201 | String signature = Descriptor.of(typeReference.getErasedDescription()); | 202 | } |
| 202 | Identifier identifier = initializer.getNameToken(); | 203 | return recurse(node, index); |
| 203 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature)); | 204 | } |
| 204 | identifierEntryCache.put(identifier.getName(), localVariableEntry); | ||
| 205 | addDeclarationToUnmatched(identifier.getName(), index); | ||
| 206 | index.addDeclaration(identifier, localVariableEntry); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | return recurse(node, index); | ||
| 210 | } | ||
| 211 | } | 205 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 2a212222..11482163 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.strobel.assembler.metadata.FieldDefinition; | 14 | import com.strobel.assembler.metadata.FieldDefinition; |
| @@ -20,79 +21,79 @@ import cuchaz.enigma.mapping.*; | |||
| 20 | 21 | ||
| 21 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 22 | public class SourceIndexClassVisitor extends SourceIndexVisitor { |
| 22 | 23 | ||
| 23 | private ClassEntry classEntry; | 24 | private ClassEntry classEntry; |
| 24 | 25 | ||
| 25 | public SourceIndexClassVisitor(ClassEntry classEntry) { | 26 | public SourceIndexClassVisitor(ClassEntry classEntry) { |
| 26 | this.classEntry = classEntry; | 27 | this.classEntry = classEntry; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | @Override | 30 | @Override |
| 30 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { | 31 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { |
| 31 | // is this this class, or a subtype? | 32 | // is this this class, or a subtype? |
| 32 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); | 33 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); |
| 33 | ClassEntry classEntry = new ClassEntry(def.getInternalName()); | 34 | ClassEntry classEntry = new ClassEntry(def.getInternalName()); |
| 34 | if (!classEntry.equals(this.classEntry)) { | 35 | if (!classEntry.equals(this.classEntry)) { |
| 35 | // it's a sub-type, recurse | 36 | // it's a sub-type, recurse |
| 36 | index.addDeclaration(node.getNameToken(), classEntry); | 37 | index.addDeclaration(node.getNameToken(), classEntry); |
| 37 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); | 38 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | return recurse(node, index); | 41 | return recurse(node, index); |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | @Override | 44 | @Override |
| 44 | public Void visitSimpleType(SimpleType node, SourceIndex index) { | 45 | public Void visitSimpleType(SimpleType node, SourceIndex index) { |
| 45 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); | 46 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); |
| 46 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { | 47 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { |
| 47 | ClassEntry classEntry = new ClassEntry(ref.getInternalName()); | 48 | ClassEntry classEntry = new ClassEntry(ref.getInternalName()); |
| 48 | index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); | 49 | index.addReference(node.getIdentifierToken(), classEntry, this.classEntry); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | return recurse(node, index); | 52 | return recurse(node, index); |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | @Override | 55 | @Override |
| 55 | public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { | 56 | public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { |
| 56 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); | 57 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); |
| 57 | BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); | 58 | BehaviorEntry behaviorEntry = ProcyonEntryFactory.getBehaviorEntry(def); |
| 58 | AstNode tokenNode = node.getNameToken(); | 59 | AstNode tokenNode = node.getNameToken(); |
| 59 | if (behaviorEntry instanceof ConstructorEntry) { | 60 | if (behaviorEntry instanceof ConstructorEntry) { |
| 60 | ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; | 61 | ConstructorEntry constructorEntry = (ConstructorEntry) behaviorEntry; |
| 61 | if (constructorEntry.isStatic()) { | 62 | if (constructorEntry.isStatic()) { |
| 62 | // for static initializers, check elsewhere for the token node | 63 | // for static initializers, check elsewhere for the token node |
| 63 | tokenNode = node.getModifiers().firstOrNullObject(); | 64 | tokenNode = node.getModifiers().firstOrNullObject(); |
| 64 | } | 65 | } |
| 65 | } | 66 | } |
| 66 | index.addDeclaration(tokenNode, behaviorEntry); | 67 | index.addDeclaration(tokenNode, behaviorEntry); |
| 67 | return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); | 68 | return node.acceptVisitor(new SourceIndexBehaviorVisitor(behaviorEntry), index); |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | @Override | 71 | @Override |
| 71 | public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { | 72 | public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { |
| 72 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); | 73 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); |
| 73 | ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); | 74 | ConstructorEntry constructorEntry = ProcyonEntryFactory.getConstructorEntry(def); |
| 74 | index.addDeclaration(node.getNameToken(), constructorEntry); | 75 | index.addDeclaration(node.getNameToken(), constructorEntry); |
| 75 | return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); | 76 | return node.acceptVisitor(new SourceIndexBehaviorVisitor(constructorEntry), index); |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | @Override | 79 | @Override |
| 79 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { | 80 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { |
| 80 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 81 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 81 | FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); | 82 | FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); |
| 82 | assert (node.getVariables().size() == 1); | 83 | assert (node.getVariables().size() == 1); |
| 83 | VariableInitializer variable = node.getVariables().firstOrNullObject(); | 84 | VariableInitializer variable = node.getVariables().firstOrNullObject(); |
| 84 | index.addDeclaration(variable.getNameToken(), fieldEntry); | 85 | index.addDeclaration(variable.getNameToken(), fieldEntry); |
| 85 | 86 | ||
| 86 | return recurse(node, index); | 87 | return recurse(node, index); |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 89 | @Override | 90 | @Override |
| 90 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { | 91 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { |
| 91 | // treat enum declarations as field declarations | 92 | // treat enum declarations as field declarations |
| 92 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 93 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 93 | FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); | 94 | FieldEntry fieldEntry = ProcyonEntryFactory.getFieldEntry(def); |
| 94 | index.addDeclaration(node.getNameToken(), fieldEntry); | 95 | index.addDeclaration(node.getNameToken(), fieldEntry); |
| 95 | 96 | ||
| 96 | return recurse(node, index); | 97 | return recurse(node, index); |
| 97 | } | 98 | } |
| 98 | } | 99 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java index 40381f43..a94a55b7 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java | |||
| @@ -8,374 +8,374 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.strobel.assembler.metadata.TypeDefinition; | 14 | import com.strobel.assembler.metadata.TypeDefinition; |
| 14 | import com.strobel.decompiler.languages.java.ast.*; | 15 | import com.strobel.decompiler.languages.java.ast.*; |
| 15 | import com.strobel.decompiler.patterns.Pattern; | 16 | import com.strobel.decompiler.patterns.Pattern; |
| 16 | |||
| 17 | import cuchaz.enigma.mapping.ClassEntry; | 17 | import cuchaz.enigma.mapping.ClassEntry; |
| 18 | 18 | ||
| 19 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { | 19 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { |
| 20 | 20 | ||
| 21 | @Override | 21 | @Override |
| 22 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { | 22 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { |
| 23 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); | 23 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); |
| 24 | ClassEntry classEntry = new ClassEntry(def.getInternalName()); | 24 | ClassEntry classEntry = new ClassEntry(def.getInternalName()); |
| 25 | index.addDeclaration(node.getNameToken(), classEntry); | 25 | index.addDeclaration(node.getNameToken(), classEntry); |
| 26 | 26 | ||
| 27 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); | 27 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | protected Void recurse(AstNode node, SourceIndex index) { | 30 | protected Void recurse(AstNode node, SourceIndex index) { |
| 31 | for (final AstNode child : node.getChildren()) { | 31 | for (final AstNode child : node.getChildren()) { |
| 32 | child.acceptVisitor(this, index); | 32 | child.acceptVisitor(this, index); |
| 33 | } | 33 | } |
| 34 | return null; | 34 | return null; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | @Override | 37 | @Override |
| 38 | public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { | 38 | public Void visitMethodDeclaration(MethodDeclaration node, SourceIndex index) { |
| 39 | return recurse(node, index); | 39 | return recurse(node, index); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | @Override | 42 | @Override |
| 43 | public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { | 43 | public Void visitConstructorDeclaration(ConstructorDeclaration node, SourceIndex index) { |
| 44 | return recurse(node, index); | 44 | return recurse(node, index); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | @Override | 47 | @Override |
| 48 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { | 48 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { |
| 49 | return recurse(node, index); | 49 | return recurse(node, index); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | @Override | 52 | @Override |
| 53 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { | 53 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { |
| 54 | return recurse(node, index); | 54 | return recurse(node, index); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | @Override | 57 | @Override |
| 58 | public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { | 58 | public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { |
| 59 | return recurse(node, index); | 59 | return recurse(node, index); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | @Override | 62 | @Override |
| 63 | public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { | 63 | public Void visitInvocationExpression(InvocationExpression node, SourceIndex index) { |
| 64 | return recurse(node, index); | 64 | return recurse(node, index); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | @Override | 67 | @Override |
| 68 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { | 68 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { |
| 69 | return recurse(node, index); | 69 | return recurse(node, index); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | @Override | 72 | @Override |
| 73 | public Void visitSimpleType(SimpleType node, SourceIndex index) { | 73 | public Void visitSimpleType(SimpleType node, SourceIndex index) { |
| 74 | return recurse(node, index); | 74 | return recurse(node, index); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | @Override | 77 | @Override |
| 78 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { | 78 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { |
| 79 | return recurse(node, index); | 79 | return recurse(node, index); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | @Override | 82 | @Override |
| 83 | public Void visitComment(Comment node, SourceIndex index) { | 83 | public Void visitComment(Comment node, SourceIndex index) { |
| 84 | return recurse(node, index); | 84 | return recurse(node, index); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | @Override | 87 | @Override |
| 88 | public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { | 88 | public Void visitPatternPlaceholder(AstNode node, Pattern pattern, SourceIndex index) { |
| 89 | return recurse(node, index); | 89 | return recurse(node, index); |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | @Override | 92 | @Override |
| 93 | public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { | 93 | public Void visitTypeReference(TypeReferenceExpression node, SourceIndex index) { |
| 94 | return recurse(node, index); | 94 | return recurse(node, index); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | @Override | 97 | @Override |
| 98 | public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { | 98 | public Void visitJavaTokenNode(JavaTokenNode node, SourceIndex index) { |
| 99 | return recurse(node, index); | 99 | return recurse(node, index); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | @Override | 102 | @Override |
| 103 | public Void visitIdentifier(Identifier node, SourceIndex index) { | 103 | public Void visitIdentifier(Identifier node, SourceIndex index) { |
| 104 | return recurse(node, index); | 104 | return recurse(node, index); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | @Override | 107 | @Override |
| 108 | public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { | 108 | public Void visitNullReferenceExpression(NullReferenceExpression node, SourceIndex index) { |
| 109 | return recurse(node, index); | 109 | return recurse(node, index); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | @Override | 112 | @Override |
| 113 | public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { | 113 | public Void visitThisReferenceExpression(ThisReferenceExpression node, SourceIndex index) { |
| 114 | return recurse(node, index); | 114 | return recurse(node, index); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | @Override | 117 | @Override |
| 118 | public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { | 118 | public Void visitSuperReferenceExpression(SuperReferenceExpression node, SourceIndex index) { |
| 119 | return recurse(node, index); | 119 | return recurse(node, index); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | @Override | 122 | @Override |
| 123 | public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { | 123 | public Void visitClassOfExpression(ClassOfExpression node, SourceIndex index) { |
| 124 | return recurse(node, index); | 124 | return recurse(node, index); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | @Override | 127 | @Override |
| 128 | public Void visitBlockStatement(BlockStatement node, SourceIndex index) { | 128 | public Void visitBlockStatement(BlockStatement node, SourceIndex index) { |
| 129 | return recurse(node, index); | 129 | return recurse(node, index); |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | @Override | 132 | @Override |
| 133 | public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { | 133 | public Void visitExpressionStatement(ExpressionStatement node, SourceIndex index) { |
| 134 | return recurse(node, index); | 134 | return recurse(node, index); |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | @Override | 137 | @Override |
| 138 | public Void visitBreakStatement(BreakStatement node, SourceIndex index) { | 138 | public Void visitBreakStatement(BreakStatement node, SourceIndex index) { |
| 139 | return recurse(node, index); | 139 | return recurse(node, index); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | @Override | 142 | @Override |
| 143 | public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { | 143 | public Void visitContinueStatement(ContinueStatement node, SourceIndex index) { |
| 144 | return recurse(node, index); | 144 | return recurse(node, index); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | @Override | 147 | @Override |
| 148 | public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { | 148 | public Void visitDoWhileStatement(DoWhileStatement node, SourceIndex index) { |
| 149 | return recurse(node, index); | 149 | return recurse(node, index); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | @Override | 152 | @Override |
| 153 | public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { | 153 | public Void visitEmptyStatement(EmptyStatement node, SourceIndex index) { |
| 154 | return recurse(node, index); | 154 | return recurse(node, index); |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | @Override | 157 | @Override |
| 158 | public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { | 158 | public Void visitIfElseStatement(IfElseStatement node, SourceIndex index) { |
| 159 | return recurse(node, index); | 159 | return recurse(node, index); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | @Override | 162 | @Override |
| 163 | public Void visitLabelStatement(LabelStatement node, SourceIndex index) { | 163 | public Void visitLabelStatement(LabelStatement node, SourceIndex index) { |
| 164 | return recurse(node, index); | 164 | return recurse(node, index); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | @Override | 167 | @Override |
| 168 | public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { | 168 | public Void visitLabeledStatement(LabeledStatement node, SourceIndex index) { |
| 169 | return recurse(node, index); | 169 | return recurse(node, index); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | @Override | 172 | @Override |
| 173 | public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { | 173 | public Void visitReturnStatement(ReturnStatement node, SourceIndex index) { |
| 174 | return recurse(node, index); | 174 | return recurse(node, index); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | @Override | 177 | @Override |
| 178 | public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { | 178 | public Void visitSwitchStatement(SwitchStatement node, SourceIndex index) { |
| 179 | return recurse(node, index); | 179 | return recurse(node, index); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | @Override | 182 | @Override |
| 183 | public Void visitSwitchSection(SwitchSection node, SourceIndex index) { | 183 | public Void visitSwitchSection(SwitchSection node, SourceIndex index) { |
| 184 | return recurse(node, index); | 184 | return recurse(node, index); |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | @Override | 187 | @Override |
| 188 | public Void visitCaseLabel(CaseLabel node, SourceIndex index) { | 188 | public Void visitCaseLabel(CaseLabel node, SourceIndex index) { |
| 189 | return recurse(node, index); | 189 | return recurse(node, index); |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | @Override | 192 | @Override |
| 193 | public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { | 193 | public Void visitThrowStatement(ThrowStatement node, SourceIndex index) { |
| 194 | return recurse(node, index); | 194 | return recurse(node, index); |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | @Override | 197 | @Override |
| 198 | public Void visitCatchClause(CatchClause node, SourceIndex index) { | 198 | public Void visitCatchClause(CatchClause node, SourceIndex index) { |
| 199 | return recurse(node, index); | 199 | return recurse(node, index); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | @Override | 202 | @Override |
| 203 | public Void visitAnnotation(Annotation node, SourceIndex index) { | 203 | public Void visitAnnotation(Annotation node, SourceIndex index) { |
| 204 | return recurse(node, index); | 204 | return recurse(node, index); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | @Override | 207 | @Override |
| 208 | public Void visitNewLine(NewLineNode node, SourceIndex index) { | 208 | public Void visitNewLine(NewLineNode node, SourceIndex index) { |
| 209 | return recurse(node, index); | 209 | return recurse(node, index); |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | @Override | 212 | @Override |
| 213 | public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { | 213 | public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) { |
| 214 | return recurse(node, index); | 214 | return recurse(node, index); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | @Override | 217 | @Override |
| 218 | public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { | 218 | public Void visitVariableInitializer(VariableInitializer node, SourceIndex index) { |
| 219 | return recurse(node, index); | 219 | return recurse(node, index); |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | @Override | 222 | @Override |
| 223 | public Void visitText(TextNode node, SourceIndex index) { | 223 | public Void visitText(TextNode node, SourceIndex index) { |
| 224 | return recurse(node, index); | 224 | return recurse(node, index); |
| 225 | } | 225 | } |
| 226 | 226 | ||
| 227 | @Override | 227 | @Override |
| 228 | public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { | 228 | public Void visitImportDeclaration(ImportDeclaration node, SourceIndex index) { |
| 229 | return recurse(node, index); | 229 | return recurse(node, index); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | @Override | 232 | @Override |
| 233 | public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { | 233 | public Void visitInitializerBlock(InstanceInitializer node, SourceIndex index) { |
| 234 | return recurse(node, index); | 234 | return recurse(node, index); |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | @Override | 237 | @Override |
| 238 | public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { | 238 | public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, SourceIndex index) { |
| 239 | return recurse(node, index); | 239 | return recurse(node, index); |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | @Override | 242 | @Override |
| 243 | public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { | 243 | public Void visitCompilationUnit(CompilationUnit node, SourceIndex index) { |
| 244 | return recurse(node, index); | 244 | return recurse(node, index); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | @Override | 247 | @Override |
| 248 | public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { | 248 | public Void visitPackageDeclaration(PackageDeclaration node, SourceIndex index) { |
| 249 | return recurse(node, index); | 249 | return recurse(node, index); |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | @Override | 252 | @Override |
| 253 | public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { | 253 | public Void visitArraySpecifier(ArraySpecifier node, SourceIndex index) { |
| 254 | return recurse(node, index); | 254 | return recurse(node, index); |
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | @Override | 257 | @Override |
| 258 | public Void visitComposedType(ComposedType node, SourceIndex index) { | 258 | public Void visitComposedType(ComposedType node, SourceIndex index) { |
| 259 | return recurse(node, index); | 259 | return recurse(node, index); |
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | @Override | 262 | @Override |
| 263 | public Void visitWhileStatement(WhileStatement node, SourceIndex index) { | 263 | public Void visitWhileStatement(WhileStatement node, SourceIndex index) { |
| 264 | return recurse(node, index); | 264 | return recurse(node, index); |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | @Override | 267 | @Override |
| 268 | public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { | 268 | public Void visitPrimitiveExpression(PrimitiveExpression node, SourceIndex index) { |
| 269 | return recurse(node, index); | 269 | return recurse(node, index); |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | @Override | 272 | @Override |
| 273 | public Void visitCastExpression(CastExpression node, SourceIndex index) { | 273 | public Void visitCastExpression(CastExpression node, SourceIndex index) { |
| 274 | return recurse(node, index); | 274 | return recurse(node, index); |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | @Override | 277 | @Override |
| 278 | public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { | 278 | public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, SourceIndex index) { |
| 279 | return recurse(node, index); | 279 | return recurse(node, index); |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | @Override | 282 | @Override |
| 283 | public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { | 283 | public Void visitInstanceOfExpression(InstanceOfExpression node, SourceIndex index) { |
| 284 | return recurse(node, index); | 284 | return recurse(node, index); |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | @Override | 287 | @Override |
| 288 | public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { | 288 | public Void visitIndexerExpression(IndexerExpression node, SourceIndex index) { |
| 289 | return recurse(node, index); | 289 | return recurse(node, index); |
| 290 | } | 290 | } |
| 291 | 291 | ||
| 292 | @Override | 292 | @Override |
| 293 | public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { | 293 | public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, SourceIndex index) { |
| 294 | return recurse(node, index); | 294 | return recurse(node, index); |
| 295 | } | 295 | } |
| 296 | 296 | ||
| 297 | @Override | 297 | @Override |
| 298 | public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { | 298 | public Void visitConditionalExpression(ConditionalExpression node, SourceIndex index) { |
| 299 | return recurse(node, index); | 299 | return recurse(node, index); |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | @Override | 302 | @Override |
| 303 | public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { | 303 | public Void visitArrayInitializerExpression(ArrayInitializerExpression node, SourceIndex index) { |
| 304 | return recurse(node, index); | 304 | return recurse(node, index); |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | @Override | 307 | @Override |
| 308 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { | 308 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { |
| 309 | return recurse(node, index); | 309 | return recurse(node, index); |
| 310 | } | 310 | } |
| 311 | 311 | ||
| 312 | @Override | 312 | @Override |
| 313 | public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { | 313 | public Void visitArrayCreationExpression(ArrayCreationExpression node, SourceIndex index) { |
| 314 | return recurse(node, index); | 314 | return recurse(node, index); |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | @Override | 317 | @Override |
| 318 | public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { | 318 | public Void visitAssignmentExpression(AssignmentExpression node, SourceIndex index) { |
| 319 | return recurse(node, index); | 319 | return recurse(node, index); |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | @Override | 322 | @Override |
| 323 | public Void visitForStatement(ForStatement node, SourceIndex index) { | 323 | public Void visitForStatement(ForStatement node, SourceIndex index) { |
| 324 | return recurse(node, index); | 324 | return recurse(node, index); |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | @Override | 327 | @Override |
| 328 | public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { | 328 | public Void visitForEachStatement(ForEachStatement node, SourceIndex index) { |
| 329 | return recurse(node, index); | 329 | return recurse(node, index); |
| 330 | } | 330 | } |
| 331 | 331 | ||
| 332 | @Override | 332 | @Override |
| 333 | public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { | 333 | public Void visitTryCatchStatement(TryCatchStatement node, SourceIndex index) { |
| 334 | return recurse(node, index); | 334 | return recurse(node, index); |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | @Override | 337 | @Override |
| 338 | public Void visitGotoStatement(GotoStatement node, SourceIndex index) { | 338 | public Void visitGotoStatement(GotoStatement node, SourceIndex index) { |
| 339 | return recurse(node, index); | 339 | return recurse(node, index); |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | @Override | 342 | @Override |
| 343 | public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { | 343 | public Void visitParenthesizedExpression(ParenthesizedExpression node, SourceIndex index) { |
| 344 | return recurse(node, index); | 344 | return recurse(node, index); |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | @Override | 347 | @Override |
| 348 | public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { | 348 | public Void visitSynchronizedStatement(SynchronizedStatement node, SourceIndex index) { |
| 349 | return recurse(node, index); | 349 | return recurse(node, index); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | @Override | 352 | @Override |
| 353 | public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { | 353 | public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, SourceIndex index) { |
| 354 | return recurse(node, index); | 354 | return recurse(node, index); |
| 355 | } | 355 | } |
| 356 | 356 | ||
| 357 | @Override | 357 | @Override |
| 358 | public Void visitWildcardType(WildcardType node, SourceIndex index) { | 358 | public Void visitWildcardType(WildcardType node, SourceIndex index) { |
| 359 | return recurse(node, index); | 359 | return recurse(node, index); |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | @Override | 362 | @Override |
| 363 | public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { | 363 | public Void visitMethodGroupExpression(MethodGroupExpression node, SourceIndex index) { |
| 364 | return recurse(node, index); | 364 | return recurse(node, index); |
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | @Override | 367 | @Override |
| 368 | public Void visitAssertStatement(AssertStatement node, SourceIndex index) { | 368 | public Void visitAssertStatement(AssertStatement node, SourceIndex index) { |
| 369 | return recurse(node, index); | 369 | return recurse(node, index); |
| 370 | } | 370 | } |
| 371 | 371 | ||
| 372 | @Override | 372 | @Override |
| 373 | public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { | 373 | public Void visitLambdaExpression(LambdaExpression node, SourceIndex index) { |
| 374 | return recurse(node, index); | 374 | return recurse(node, index); |
| 375 | } | 375 | } |
| 376 | 376 | ||
| 377 | @Override | 377 | @Override |
| 378 | public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { | 378 | public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, SourceIndex index) { |
| 379 | return recurse(node, index); | 379 | return recurse(node, index); |
| 380 | } | 380 | } |
| 381 | } | 381 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java index 42f4660a..266d2027 100644 --- a/src/main/java/cuchaz/enigma/analysis/Token.java +++ b/src/main/java/cuchaz/enigma/analysis/Token.java | |||
| @@ -8,48 +8,48 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | public class Token implements Comparable<Token> { | 14 | public class Token implements Comparable<Token> { |
| 14 | 15 | ||
| 15 | public int start; | 16 | public int start; |
| 16 | public int end; | 17 | public int end; |
| 17 | public String text; | 18 | public String text; |
| 18 | 19 | ||
| 19 | public Token(int start, int end, String source) { | 20 | public Token(int start, int end, String source) { |
| 20 | this.start = start; | 21 | this.start = start; |
| 21 | this.end = end; | 22 | this.end = end; |
| 22 | if (source != null) { | 23 | if (source != null) { |
| 23 | this.text = source.substring(start, end); | 24 | this.text = source.substring(start, end); |
| 24 | } | 25 | } |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | public boolean contains(int pos) { | 28 | public boolean contains(int pos) { |
| 28 | return pos >= start && pos <= end; | 29 | return pos >= start && pos <= end; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | @Override | 32 | @Override |
| 32 | public int compareTo(Token other) { | 33 | public int compareTo(Token other) { |
| 33 | return start - other.start; | 34 | return start - other.start; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | @Override | 37 | @Override |
| 37 | public boolean equals(Object other) { | 38 | public boolean equals(Object other) { |
| 38 | return other instanceof Token && equals((Token) other); | 39 | return other instanceof Token && equals((Token) other); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | @Override | 42 | @Override |
| 42 | public int hashCode() | 43 | public int hashCode() { |
| 43 | { | 44 | return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); |
| 44 | return Integer.hashCode(start) + Integer.hashCode(end) + (text != null ? text.hashCode() : 0); | 45 | } |
| 45 | } | 46 | |
| 46 | 47 | public boolean equals(Token other) { | |
| 47 | public boolean equals(Token other) { | 48 | return start == other.start && end == other.end; |
| 48 | return start == other.start && end == other.end; | 49 | } |
| 49 | } | 50 | |
| 50 | 51 | @Override | |
| 51 | @Override | 52 | public String toString() { |
| 52 | public String toString() { | 53 | return String.format("[%d,%d]", start, end); |
| 53 | return String.format("[%d,%d]", start, end); | 54 | } |
| 54 | } | ||
| 55 | } | 55 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java index d51131f6..26be05b4 100644 --- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java | |||
| @@ -8,291 +8,288 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 14 | import com.google.common.collect.Lists; | 15 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 17 | |||
| 18 | import java.util.Collection; | ||
| 19 | import java.util.List; | ||
| 20 | import java.util.Map; | ||
| 21 | import java.util.Set; | ||
| 22 | |||
| 23 | import cuchaz.enigma.mapping.*; | 18 | import cuchaz.enigma.mapping.*; |
| 24 | import javassist.CtBehavior; | 19 | import javassist.CtBehavior; |
| 25 | import javassist.CtClass; | 20 | import javassist.CtClass; |
| 26 | import javassist.CtField; | 21 | import javassist.CtField; |
| 27 | import javassist.bytecode.Descriptor; | 22 | import javassist.bytecode.Descriptor; |
| 28 | 23 | ||
| 24 | import java.util.Collection; | ||
| 25 | import java.util.List; | ||
| 26 | import java.util.Map; | ||
| 27 | import java.util.Set; | ||
| 28 | |||
| 29 | public class TranslationIndex { | 29 | public class TranslationIndex { |
| 30 | 30 | ||
| 31 | private Map<ClassEntry, ClassEntry> superclasses; | 31 | private Map<ClassEntry, ClassEntry> superclasses; |
| 32 | private Multimap<ClassEntry, FieldEntry> fieldEntries; | 32 | private Multimap<ClassEntry, FieldEntry> fieldEntries; |
| 33 | private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; | 33 | private Multimap<ClassEntry, BehaviorEntry> behaviorEntries; |
| 34 | private Multimap<ClassEntry, ClassEntry> interfaces; | 34 | private Multimap<ClassEntry, ClassEntry> interfaces; |
| 35 | 35 | ||
| 36 | public TranslationIndex() { | 36 | public TranslationIndex() { |
| 37 | this.superclasses = Maps.newHashMap(); | 37 | this.superclasses = Maps.newHashMap(); |
| 38 | this.fieldEntries = HashMultimap.create(); | 38 | this.fieldEntries = HashMultimap.create(); |
| 39 | this.behaviorEntries = HashMultimap.create(); | 39 | this.behaviorEntries = HashMultimap.create(); |
| 40 | this.interfaces = HashMultimap.create(); | 40 | this.interfaces = HashMultimap.create(); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | public TranslationIndex(TranslationIndex other, Translator translator) { | 43 | public TranslationIndex(TranslationIndex other, Translator translator) { |
| 44 | // translate the superclasses | 44 | // translate the superclasses |
| 45 | this.superclasses = Maps.newHashMap(); | 45 | this.superclasses = Maps.newHashMap(); |
| 46 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { | 46 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.superclasses.entrySet()) { |
| 47 | this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); | 47 | this.superclasses.put(translator.translateEntry(mapEntry.getKey()), translator.translateEntry(mapEntry.getValue())); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | // translate the interfaces | 50 | // translate the interfaces |
| 51 | this.interfaces = HashMultimap.create(); | 51 | this.interfaces = HashMultimap.create(); |
| 52 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { | 52 | for (Map.Entry<ClassEntry, ClassEntry> mapEntry : other.interfaces.entries()) { |
| 53 | this.interfaces.put( | 53 | this.interfaces.put( |
| 54 | translator.translateEntry(mapEntry.getKey()), | 54 | translator.translateEntry(mapEntry.getKey()), |
| 55 | translator.translateEntry(mapEntry.getValue()) | 55 | translator.translateEntry(mapEntry.getValue()) |
| 56 | ); | 56 | ); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | // translate the fields | 59 | // translate the fields |
| 60 | this.fieldEntries = HashMultimap.create(); | 60 | this.fieldEntries = HashMultimap.create(); |
| 61 | for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { | 61 | for (Map.Entry<ClassEntry, FieldEntry> mapEntry : other.fieldEntries.entries()) { |
| 62 | this.fieldEntries.put( | 62 | this.fieldEntries.put( |
| 63 | translator.translateEntry(mapEntry.getKey()), | 63 | translator.translateEntry(mapEntry.getKey()), |
| 64 | translator.translateEntry(mapEntry.getValue()) | 64 | translator.translateEntry(mapEntry.getValue()) |
| 65 | ); | 65 | ); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | this.behaviorEntries = HashMultimap.create(); | 68 | this.behaviorEntries = HashMultimap.create(); |
| 69 | for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { | 69 | for (Map.Entry<ClassEntry, BehaviorEntry> mapEntry : other.behaviorEntries.entries()) { |
| 70 | this.behaviorEntries.put( | 70 | this.behaviorEntries.put( |
| 71 | translator.translateEntry(mapEntry.getKey()), | 71 | translator.translateEntry(mapEntry.getKey()), |
| 72 | translator.translateEntry(mapEntry.getValue()) | 72 | translator.translateEntry(mapEntry.getValue()) |
| 73 | ); | 73 | ); |
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | public void indexClass(CtClass c) { | 77 | public void indexClass(CtClass c) { |
| 78 | indexClass(c, true); | 78 | indexClass(c, true); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | public void indexClass(CtClass c, boolean indexMembers) { | 81 | public void indexClass(CtClass c, boolean indexMembers) { |
| 82 | ClassEntry classEntry = EntryFactory.getClassEntry(c); | 82 | ClassEntry classEntry = EntryFactory.getClassEntry(c); |
| 83 | if (isJre(classEntry)) { | 83 | if (isJre(classEntry)) { |
| 84 | return; | 84 | return; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | // add the superclass | 87 | // add the superclass |
| 88 | ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); | 88 | ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c); |
| 89 | if (superclassEntry != null) { | 89 | if (superclassEntry != null) { |
| 90 | this.superclasses.put(classEntry, superclassEntry); | 90 | this.superclasses.put(classEntry, superclassEntry); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | // add the interfaces | 93 | // add the interfaces |
| 94 | for (String interfaceClassName : c.getClassFile().getInterfaces()) { | 94 | for (String interfaceClassName : c.getClassFile().getInterfaces()) { |
| 95 | ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); | 95 | ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName)); |
| 96 | if (!isJre(interfaceClassEntry)) { | 96 | if (!isJre(interfaceClassEntry)) { |
| 97 | 97 | ||
| 98 | this.interfaces.put(classEntry, interfaceClassEntry); | 98 | this.interfaces.put(classEntry, interfaceClassEntry); |
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | if (indexMembers) { | 102 | if (indexMembers) { |
| 103 | // add fields | 103 | // add fields |
| 104 | for (CtField field : c.getDeclaredFields()) { | 104 | for (CtField field : c.getDeclaredFields()) { |
| 105 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); | 105 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); |
| 106 | this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); | 106 | this.fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | // add behaviors | 109 | // add behaviors |
| 110 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 110 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 111 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 111 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 112 | this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); | 112 | this.behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry); |
| 113 | } | 113 | } |
| 114 | } | 114 | } |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | public void renameClasses(Map<String, String> renames) { | 117 | public void renameClasses(Map<String, String> renames) { |
| 118 | EntryRenamer.renameClassesInMap(renames, this.superclasses); | 118 | EntryRenamer.renameClassesInMap(renames, this.superclasses); |
| 119 | EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); | 119 | EntryRenamer.renameClassesInMultimap(renames, this.fieldEntries); |
| 120 | EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); | 120 | EntryRenamer.renameClassesInMultimap(renames, this.behaviorEntries); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | public ClassEntry getSuperclass(ClassEntry classEntry) { | 123 | public ClassEntry getSuperclass(ClassEntry classEntry) { |
| 124 | return this.superclasses.get(classEntry); | 124 | return this.superclasses.get(classEntry); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | public List<ClassEntry> getAncestry(ClassEntry classEntry) { | 127 | public List<ClassEntry> getAncestry(ClassEntry classEntry) { |
| 128 | List<ClassEntry> ancestors = Lists.newArrayList(); | 128 | List<ClassEntry> ancestors = Lists.newArrayList(); |
| 129 | while (classEntry != null) { | 129 | while (classEntry != null) { |
| 130 | classEntry = getSuperclass(classEntry); | 130 | classEntry = getSuperclass(classEntry); |
| 131 | if (classEntry != null) { | 131 | if (classEntry != null) { |
| 132 | ancestors.add(classEntry); | 132 | ancestors.add(classEntry); |
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | return ancestors; | 135 | return ancestors; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | public List<ClassEntry> getSubclass(ClassEntry classEntry) { | 138 | public List<ClassEntry> getSubclass(ClassEntry classEntry) { |
| 139 | // linear search is fast enough for now | 139 | // linear search is fast enough for now |
| 140 | List<ClassEntry> subclasses = Lists.newArrayList(); | 140 | List<ClassEntry> subclasses = Lists.newArrayList(); |
| 141 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { | 141 | for (Map.Entry<ClassEntry, ClassEntry> entry : this.superclasses.entrySet()) { |
| 142 | ClassEntry subclass = entry.getKey(); | 142 | ClassEntry subclass = entry.getKey(); |
| 143 | ClassEntry superclass = entry.getValue(); | 143 | ClassEntry superclass = entry.getValue(); |
| 144 | if (classEntry.equals(superclass)) { | 144 | if (classEntry.equals(superclass)) { |
| 145 | subclasses.add(subclass); | 145 | subclasses.add(subclass); |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| 148 | return subclasses; | 148 | return subclasses; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { | 151 | public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) { |
| 152 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { | 152 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { |
| 153 | out.add(subclassEntry); | 153 | out.add(subclassEntry); |
| 154 | getSubclassesRecursively(out, subclassEntry); | 154 | getSubclassesRecursively(out, subclassEntry); |
| 155 | } | 155 | } |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { | 158 | public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) { |
| 159 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { | 159 | for (ClassEntry subclassEntry : getSubclass(classEntry)) { |
| 160 | out.add(subclassEntry.getName()); | 160 | out.add(subclassEntry.getName()); |
| 161 | getSubclassNamesRecursively(out, subclassEntry); | 161 | getSubclassNamesRecursively(out, subclassEntry); |
| 162 | } | 162 | } |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { | 165 | public Collection<Map.Entry<ClassEntry, ClassEntry>> getClassInterfaces() { |
| 166 | return this.interfaces.entries(); | 166 | return this.interfaces.entries(); |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { | 169 | public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) { |
| 170 | return this.interfaces.get(classEntry); | 170 | return this.interfaces.get(classEntry); |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | public boolean isInterface(ClassEntry classEntry) { | 173 | public boolean isInterface(ClassEntry classEntry) { |
| 174 | return this.interfaces.containsValue(classEntry); | 174 | return this.interfaces.containsValue(classEntry); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | public boolean entryExists(Entry entry) { | 177 | public boolean entryExists(Entry entry) { |
| 178 | if (entry instanceof FieldEntry) { | 178 | if (entry instanceof FieldEntry) { |
| 179 | return fieldExists((FieldEntry) entry); | 179 | return fieldExists((FieldEntry) entry); |
| 180 | } else if (entry instanceof BehaviorEntry) { | 180 | } else if (entry instanceof BehaviorEntry) { |
| 181 | return behaviorExists((BehaviorEntry) entry); | 181 | return behaviorExists((BehaviorEntry) entry); |
| 182 | } else if (entry instanceof ArgumentEntry) { | 182 | } else if (entry instanceof ArgumentEntry) { |
| 183 | return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); | 183 | return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); |
| 184 | } else if (entry instanceof LocalVariableEntry) { | 184 | } else if (entry instanceof LocalVariableEntry) { |
| 185 | return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); | 185 | return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry()); |
| 186 | } | 186 | } |
| 187 | throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); | 187 | throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | public boolean fieldExists(FieldEntry fieldEntry) { | 190 | public boolean fieldExists(FieldEntry fieldEntry) { |
| 191 | return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); | 191 | return this.fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | public boolean behaviorExists(BehaviorEntry behaviorEntry) { | 194 | public boolean behaviorExists(BehaviorEntry behaviorEntry) { |
| 195 | return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); | 195 | return this.behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | public ClassEntry resolveEntryClass(Entry entry) { | 198 | public ClassEntry resolveEntryClass(Entry entry) { |
| 199 | return resolveEntryClass(entry, false); | 199 | return resolveEntryClass(entry, false); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { | 202 | public ClassEntry resolveEntryClass(Entry entry, boolean checkSuperclassBeforeChild) { |
| 203 | if (entry instanceof ClassEntry) { | 203 | if (entry instanceof ClassEntry) { |
| 204 | return (ClassEntry) entry; | 204 | return (ClassEntry) entry; |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); | 207 | ClassEntry superclassEntry = resolveSuperclass(entry, checkSuperclassBeforeChild); |
| 208 | if (superclassEntry != null) { | 208 | if (superclassEntry != null) { |
| 209 | return superclassEntry; | 209 | return superclassEntry; |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | ClassEntry interfaceEntry = resolveInterface(entry); | 212 | ClassEntry interfaceEntry = resolveInterface(entry); |
| 213 | if (interfaceEntry != null) { | 213 | if (interfaceEntry != null) { |
| 214 | return interfaceEntry; | 214 | return interfaceEntry; |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | return null; | 217 | return null; |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { | 220 | public ClassEntry resolveSuperclass(Entry entry, boolean checkSuperclassBeforeChild) { |
| 221 | 221 | ||
| 222 | // Default case | 222 | // Default case |
| 223 | if (!checkSuperclassBeforeChild) | 223 | if (!checkSuperclassBeforeChild) |
| 224 | return resolveSuperclass(entry); | 224 | return resolveSuperclass(entry); |
| 225 | 225 | ||
| 226 | // Save the original entry | 226 | // Save the original entry |
| 227 | Entry originalEntry = entry; | 227 | Entry originalEntry = entry; |
| 228 | 228 | ||
| 229 | // Get all possible superclasses and reverse the list | 229 | // Get all possible superclasses and reverse the list |
| 230 | List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); | 230 | List<ClassEntry> superclasses = Lists.reverse(getAncestry(originalEntry.getClassEntry())); |
| 231 | 231 | ||
| 232 | boolean existInEntry = false; | 232 | boolean existInEntry = false; |
| 233 | 233 | ||
| 234 | for (ClassEntry classEntry : superclasses) | 234 | for (ClassEntry classEntry : superclasses) { |
| 235 | { | 235 | entry = entry.cloneToNewClass(classEntry); |
| 236 | entry = entry.cloneToNewClass(classEntry); | 236 | existInEntry = entryExists(entry); |
| 237 | existInEntry = entryExists(entry); | 237 | |
| 238 | 238 | // Check for possible entry in interfaces of superclasses | |
| 239 | // Check for possible entry in interfaces of superclasses | 239 | ClassEntry interfaceEntry = resolveInterface(entry); |
| 240 | ClassEntry interfaceEntry = resolveInterface(entry); | 240 | if (interfaceEntry != null) |
| 241 | if (interfaceEntry != null) | 241 | return interfaceEntry; |
| 242 | return interfaceEntry; | 242 | if (existInEntry) |
| 243 | if (existInEntry) | 243 | break; |
| 244 | break; | 244 | } |
| 245 | } | 245 | |
| 246 | 246 | // Doesn't exists in superclasses? check the child or return null | |
| 247 | // Doesn't exists in superclasses? check the child or return null | 247 | if (!existInEntry) |
| 248 | if (!existInEntry) | 248 | return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); |
| 249 | return !entryExists(originalEntry) ? null : originalEntry.getClassEntry(); | 249 | |
| 250 | 250 | return entry.getClassEntry(); | |
| 251 | return entry.getClassEntry(); | 251 | } |
| 252 | } | 252 | |
| 253 | 253 | public ClassEntry resolveSuperclass(Entry entry) { | |
| 254 | public ClassEntry resolveSuperclass(Entry entry) | 254 | // this entry could refer to a method on a class where the method is not actually implemented |
| 255 | { | 255 | // travel up the inheritance tree to find the closest implementation |
| 256 | // this entry could refer to a method on a class where the method is not actually implemented | 256 | |
| 257 | // travel up the inheritance tree to find the closest implementation | 257 | while (!entryExists(entry)) { |
| 258 | 258 | // is there a parent class? | |
| 259 | while (!entryExists(entry)) { | 259 | ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); |
| 260 | // is there a parent class? | 260 | if (superclassEntry == null) { |
| 261 | ClassEntry superclassEntry = getSuperclass(entry.getClassEntry()); | 261 | // this is probably a method from a class in a library |
| 262 | if (superclassEntry == null) { | 262 | // we can't trace the implementation up any higher unless we index the library |
| 263 | // this is probably a method from a class in a library | 263 | return null; |
| 264 | // we can't trace the implementation up any higher unless we index the library | 264 | } |
| 265 | return null; | 265 | |
| 266 | } | 266 | // move up to the parent class |
| 267 | 267 | entry = entry.cloneToNewClass(superclassEntry); | |
| 268 | // move up to the parent class | 268 | } |
| 269 | entry = entry.cloneToNewClass(superclassEntry); | 269 | return entry.getClassEntry(); |
| 270 | } | 270 | } |
| 271 | return entry.getClassEntry(); | 271 | |
| 272 | } | 272 | public ClassEntry resolveInterface(Entry entry) { |
| 273 | 273 | // the interfaces for any class is a forest | |
| 274 | public ClassEntry resolveInterface(Entry entry) { | 274 | // so let's look at all the trees |
| 275 | // the interfaces for any class is a forest | 275 | |
| 276 | // so let's look at all the trees | 276 | for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { |
| 277 | 277 | Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); | |
| 278 | for (ClassEntry interfaceEntry : this.interfaces.get(entry.getClassEntry())) { | 278 | if (subInterface != null && !subInterface.isEmpty()) { |
| 279 | Collection<ClassEntry> subInterface = this.interfaces.get(interfaceEntry); | 279 | ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); |
| 280 | if (subInterface != null && !subInterface.isEmpty()) | 280 | if (result != null) |
| 281 | { | 281 | return result; |
| 282 | ClassEntry result = resolveInterface(entry.cloneToNewClass(interfaceEntry)); | 282 | } |
| 283 | if (result != null) | 283 | ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); |
| 284 | return result; | 284 | if (resolvedClassEntry != null) { |
| 285 | } | 285 | return resolvedClassEntry; |
| 286 | ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry)); | 286 | } |
| 287 | if (resolvedClassEntry != null) { | 287 | } |
| 288 | return resolvedClassEntry; | 288 | return null; |
| 289 | } | 289 | } |
| 290 | } | 290 | |
| 291 | return null; | 291 | private boolean isJre(ClassEntry classEntry) { |
| 292 | } | 292 | String packageName = classEntry.getPackageName(); |
| 293 | 293 | return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); | |
| 294 | private boolean isJre(ClassEntry classEntry) { | 294 | } |
| 295 | String packageName = classEntry.getPackageName(); | ||
| 296 | return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax")); | ||
| 297 | } | ||
| 298 | } | 295 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java index cc025da7..c98fb9ef 100644 --- a/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/TreeDumpVisitor.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 12 | 13 | ||
| 13 | import com.strobel.componentmodel.Key; | 14 | import com.strobel.componentmodel.Key; |
| @@ -19,420 +20,420 @@ import java.nio.charset.Charset; | |||
| 19 | 20 | ||
| 20 | public class TreeDumpVisitor implements IAstVisitor<Void, Void> { | 21 | public class TreeDumpVisitor implements IAstVisitor<Void, Void> { |
| 21 | 22 | ||
| 22 | private File file; | 23 | private File file; |
| 23 | private Writer out; | 24 | private Writer out; |
| 24 | 25 | ||
| 25 | public TreeDumpVisitor(File file) { | 26 | public TreeDumpVisitor(File file) { |
| 26 | this.file = file; | 27 | this.file = file; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | @Override | 30 | @Override |
| 30 | public Void visitCompilationUnit(CompilationUnit node, Void ignored) { | 31 | public Void visitCompilationUnit(CompilationUnit node, Void ignored) { |
| 31 | try { | 32 | try { |
| 32 | out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); | 33 | out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); |
| 33 | recurse(node, ignored); | 34 | recurse(node, ignored); |
| 34 | out.close(); | 35 | out.close(); |
| 35 | return null; | 36 | return null; |
| 36 | } catch (IOException ex) { | 37 | } catch (IOException ex) { |
| 37 | throw new Error(ex); | 38 | throw new Error(ex); |
| 38 | } | 39 | } |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | private Void recurse(AstNode node, Void ignored) { | 42 | private Void recurse(AstNode node, Void ignored) { |
| 42 | // show the tree | 43 | // show the tree |
| 43 | try { | 44 | try { |
| 44 | out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); | 45 | out.write(getIndent(node) + node.getClass().getSimpleName() + " " + getText(node) + " " + dumpUserData(node) + " " + node.getRegion() + "\n"); |
| 45 | } catch (IOException ex) { | 46 | } catch (IOException ex) { |
| 46 | throw new Error(ex); | 47 | throw new Error(ex); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | // recurse | 50 | // recurse |
| 50 | for (final AstNode child : node.getChildren()) { | 51 | for (final AstNode child : node.getChildren()) { |
| 51 | child.acceptVisitor(this, ignored); | 52 | child.acceptVisitor(this, ignored); |
| 52 | } | 53 | } |
| 53 | return null; | 54 | return null; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | private String getText(AstNode node) { | 57 | private String getText(AstNode node) { |
| 57 | if (node instanceof Identifier) { | 58 | if (node instanceof Identifier) { |
| 58 | return "\"" + ((Identifier) node).getName() + "\""; | 59 | return "\"" + ((Identifier) node).getName() + "\""; |
| 59 | } | 60 | } |
| 60 | return ""; | 61 | return ""; |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | private String dumpUserData(AstNode node) { | 64 | private String dumpUserData(AstNode node) { |
| 64 | StringBuilder buf = new StringBuilder(); | 65 | StringBuilder buf = new StringBuilder(); |
| 65 | for (Key<?> key : Keys.ALL_KEYS) { | 66 | for (Key<?> key : Keys.ALL_KEYS) { |
| 66 | Object val = node.getUserData(key); | 67 | Object val = node.getUserData(key); |
| 67 | if (val != null) { | 68 | if (val != null) { |
| 68 | buf.append(String.format(" [%s=%s]", key, val)); | 69 | buf.append(String.format(" [%s=%s]", key, val)); |
| 69 | } | 70 | } |
| 70 | } | 71 | } |
| 71 | return buf.toString(); | 72 | return buf.toString(); |
| 72 | } | 73 | } |
| 73 | 74 | ||
| 74 | private String getIndent(AstNode node) { | 75 | private String getIndent(AstNode node) { |
| 75 | StringBuilder buf = new StringBuilder(); | 76 | StringBuilder buf = new StringBuilder(); |
| 76 | int depth = getDepth(node); | 77 | int depth = getDepth(node); |
| 77 | for (int i = 0; i < depth; i++) { | 78 | for (int i = 0; i < depth; i++) { |
| 78 | buf.append("\t"); | 79 | buf.append("\t"); |
| 79 | } | 80 | } |
| 80 | return buf.toString(); | 81 | return buf.toString(); |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | private int getDepth(AstNode node) { | 84 | private int getDepth(AstNode node) { |
| 84 | int depth = -1; | 85 | int depth = -1; |
| 85 | while (node != null) { | 86 | while (node != null) { |
| 86 | depth++; | 87 | depth++; |
| 87 | node = node.getParent(); | 88 | node = node.getParent(); |
| 88 | } | 89 | } |
| 89 | return depth; | 90 | return depth; |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | // OVERRIDES WE DON'T CARE ABOUT | 93 | // OVERRIDES WE DON'T CARE ABOUT |
| 93 | 94 | ||
| 94 | @Override | 95 | @Override |
| 95 | public Void visitInvocationExpression(InvocationExpression node, Void ignored) { | 96 | public Void visitInvocationExpression(InvocationExpression node, Void ignored) { |
| 96 | return recurse(node, ignored); | 97 | return recurse(node, ignored); |
| 97 | } | 98 | } |
| 98 | 99 | ||
| 99 | @Override | 100 | @Override |
| 100 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { | 101 | public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void ignored) { |
| 101 | return recurse(node, ignored); | 102 | return recurse(node, ignored); |
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | @Override | 105 | @Override |
| 105 | public Void visitSimpleType(SimpleType node, Void ignored) { | 106 | public Void visitSimpleType(SimpleType node, Void ignored) { |
| 106 | return recurse(node, ignored); | 107 | return recurse(node, ignored); |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | @Override | 110 | @Override |
| 110 | public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { | 111 | public Void visitMethodDeclaration(MethodDeclaration node, Void ignored) { |
| 111 | return recurse(node, ignored); | 112 | return recurse(node, ignored); |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | @Override | 115 | @Override |
| 115 | public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { | 116 | public Void visitConstructorDeclaration(ConstructorDeclaration node, Void ignored) { |
| 116 | return recurse(node, ignored); | 117 | return recurse(node, ignored); |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | @Override | 120 | @Override |
| 120 | public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { | 121 | public Void visitParameterDeclaration(ParameterDeclaration node, Void ignored) { |
| 121 | return recurse(node, ignored); | 122 | return recurse(node, ignored); |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | @Override | 125 | @Override |
| 125 | public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { | 126 | public Void visitFieldDeclaration(FieldDeclaration node, Void ignored) { |
| 126 | return recurse(node, ignored); | 127 | return recurse(node, ignored); |
| 127 | } | 128 | } |
| 128 | 129 | ||
| 129 | @Override | 130 | @Override |
| 130 | public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { | 131 | public Void visitTypeDeclaration(TypeDeclaration node, Void ignored) { |
| 131 | return recurse(node, ignored); | 132 | return recurse(node, ignored); |
| 132 | } | 133 | } |
| 133 | 134 | ||
| 134 | @Override | 135 | @Override |
| 135 | public Void visitComment(Comment node, Void ignored) { | 136 | public Void visitComment(Comment node, Void ignored) { |
| 136 | return recurse(node, ignored); | 137 | return recurse(node, ignored); |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 139 | @Override | 140 | @Override |
| 140 | public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { | 141 | public Void visitPatternPlaceholder(AstNode node, Pattern pattern, Void ignored) { |
| 141 | return recurse(node, ignored); | 142 | return recurse(node, ignored); |
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | @Override | 145 | @Override |
| 145 | public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { | 146 | public Void visitTypeReference(TypeReferenceExpression node, Void ignored) { |
| 146 | return recurse(node, ignored); | 147 | return recurse(node, ignored); |
| 147 | } | 148 | } |
| 148 | 149 | ||
| 149 | @Override | 150 | @Override |
| 150 | public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { | 151 | public Void visitJavaTokenNode(JavaTokenNode node, Void ignored) { |
| 151 | return recurse(node, ignored); | 152 | return recurse(node, ignored); |
| 152 | } | 153 | } |
| 153 | 154 | ||
| 154 | @Override | 155 | @Override |
| 155 | public Void visitIdentifier(Identifier node, Void ignored) { | 156 | public Void visitIdentifier(Identifier node, Void ignored) { |
| 156 | return recurse(node, ignored); | 157 | return recurse(node, ignored); |
| 157 | } | 158 | } |
| 158 | 159 | ||
| 159 | @Override | 160 | @Override |
| 160 | public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { | 161 | public Void visitNullReferenceExpression(NullReferenceExpression node, Void ignored) { |
| 161 | return recurse(node, ignored); | 162 | return recurse(node, ignored); |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 164 | @Override | 165 | @Override |
| 165 | public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { | 166 | public Void visitThisReferenceExpression(ThisReferenceExpression node, Void ignored) { |
| 166 | return recurse(node, ignored); | 167 | return recurse(node, ignored); |
| 167 | } | 168 | } |
| 168 | 169 | ||
| 169 | @Override | 170 | @Override |
| 170 | public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { | 171 | public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void ignored) { |
| 171 | return recurse(node, ignored); | 172 | return recurse(node, ignored); |
| 172 | } | 173 | } |
| 173 | 174 | ||
| 174 | @Override | 175 | @Override |
| 175 | public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { | 176 | public Void visitClassOfExpression(ClassOfExpression node, Void ignored) { |
| 176 | return recurse(node, ignored); | 177 | return recurse(node, ignored); |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | @Override | 180 | @Override |
| 180 | public Void visitBlockStatement(BlockStatement node, Void ignored) { | 181 | public Void visitBlockStatement(BlockStatement node, Void ignored) { |
| 181 | return recurse(node, ignored); | 182 | return recurse(node, ignored); |
| 182 | } | 183 | } |
| 183 | 184 | ||
| 184 | @Override | 185 | @Override |
| 185 | public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { | 186 | public Void visitExpressionStatement(ExpressionStatement node, Void ignored) { |
| 186 | return recurse(node, ignored); | 187 | return recurse(node, ignored); |
| 187 | } | 188 | } |
| 188 | 189 | ||
| 189 | @Override | 190 | @Override |
| 190 | public Void visitBreakStatement(BreakStatement node, Void ignored) { | 191 | public Void visitBreakStatement(BreakStatement node, Void ignored) { |
| 191 | return recurse(node, ignored); | 192 | return recurse(node, ignored); |
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | @Override | 195 | @Override |
| 195 | public Void visitContinueStatement(ContinueStatement node, Void ignored) { | 196 | public Void visitContinueStatement(ContinueStatement node, Void ignored) { |
| 196 | return recurse(node, ignored); | 197 | return recurse(node, ignored); |
| 197 | } | 198 | } |
| 198 | 199 | ||
| 199 | @Override | 200 | @Override |
| 200 | public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { | 201 | public Void visitDoWhileStatement(DoWhileStatement node, Void ignored) { |
| 201 | return recurse(node, ignored); | 202 | return recurse(node, ignored); |
| 202 | } | 203 | } |
| 203 | 204 | ||
| 204 | @Override | 205 | @Override |
| 205 | public Void visitEmptyStatement(EmptyStatement node, Void ignored) { | 206 | public Void visitEmptyStatement(EmptyStatement node, Void ignored) { |
| 206 | return recurse(node, ignored); | 207 | return recurse(node, ignored); |
| 207 | } | 208 | } |
| 208 | 209 | ||
| 209 | @Override | 210 | @Override |
| 210 | public Void visitIfElseStatement(IfElseStatement node, Void ignored) { | 211 | public Void visitIfElseStatement(IfElseStatement node, Void ignored) { |
| 211 | return recurse(node, ignored); | 212 | return recurse(node, ignored); |
| 212 | } | 213 | } |
| 213 | 214 | ||
| 214 | @Override | 215 | @Override |
| 215 | public Void visitLabelStatement(LabelStatement node, Void ignored) { | 216 | public Void visitLabelStatement(LabelStatement node, Void ignored) { |
| 216 | return recurse(node, ignored); | 217 | return recurse(node, ignored); |
| 217 | } | 218 | } |
| 218 | 219 | ||
| 219 | @Override | 220 | @Override |
| 220 | public Void visitLabeledStatement(LabeledStatement node, Void ignored) { | 221 | public Void visitLabeledStatement(LabeledStatement node, Void ignored) { |
| 221 | return recurse(node, ignored); | 222 | return recurse(node, ignored); |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | @Override | 225 | @Override |
| 225 | public Void visitReturnStatement(ReturnStatement node, Void ignored) { | 226 | public Void visitReturnStatement(ReturnStatement node, Void ignored) { |
| 226 | return recurse(node, ignored); | 227 | return recurse(node, ignored); |
| 227 | } | 228 | } |
| 228 | 229 | ||
| 229 | @Override | 230 | @Override |
| 230 | public Void visitSwitchStatement(SwitchStatement node, Void ignored) { | 231 | public Void visitSwitchStatement(SwitchStatement node, Void ignored) { |
| 231 | return recurse(node, ignored); | 232 | return recurse(node, ignored); |
| 232 | } | 233 | } |
| 233 | 234 | ||
| 234 | @Override | 235 | @Override |
| 235 | public Void visitSwitchSection(SwitchSection node, Void ignored) { | 236 | public Void visitSwitchSection(SwitchSection node, Void ignored) { |
| 236 | return recurse(node, ignored); | 237 | return recurse(node, ignored); |
| 237 | } | 238 | } |
| 238 | 239 | ||
| 239 | @Override | 240 | @Override |
| 240 | public Void visitCaseLabel(CaseLabel node, Void ignored) { | 241 | public Void visitCaseLabel(CaseLabel node, Void ignored) { |
| 241 | return recurse(node, ignored); | 242 | return recurse(node, ignored); |
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | @Override | 245 | @Override |
| 245 | public Void visitThrowStatement(ThrowStatement node, Void ignored) { | 246 | public Void visitThrowStatement(ThrowStatement node, Void ignored) { |
| 246 | return recurse(node, ignored); | 247 | return recurse(node, ignored); |
| 247 | } | 248 | } |
| 248 | 249 | ||
| 249 | @Override | 250 | @Override |
| 250 | public Void visitCatchClause(CatchClause node, Void ignored) { | 251 | public Void visitCatchClause(CatchClause node, Void ignored) { |
| 251 | return recurse(node, ignored); | 252 | return recurse(node, ignored); |
| 252 | } | 253 | } |
| 253 | 254 | ||
| 254 | @Override | 255 | @Override |
| 255 | public Void visitAnnotation(Annotation node, Void ignored) { | 256 | public Void visitAnnotation(Annotation node, Void ignored) { |
| 256 | return recurse(node, ignored); | 257 | return recurse(node, ignored); |
| 257 | } | 258 | } |
| 258 | 259 | ||
| 259 | @Override | 260 | @Override |
| 260 | public Void visitNewLine(NewLineNode node, Void ignored) { | 261 | public Void visitNewLine(NewLineNode node, Void ignored) { |
| 261 | return recurse(node, ignored); | 262 | return recurse(node, ignored); |
| 262 | } | 263 | } |
| 263 | 264 | ||
| 264 | @Override | 265 | @Override |
| 265 | public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { | 266 | public Void visitVariableDeclaration(VariableDeclarationStatement node, Void ignored) { |
| 266 | return recurse(node, ignored); | 267 | return recurse(node, ignored); |
| 267 | } | 268 | } |
| 268 | 269 | ||
| 269 | @Override | 270 | @Override |
| 270 | public Void visitVariableInitializer(VariableInitializer node, Void ignored) { | 271 | public Void visitVariableInitializer(VariableInitializer node, Void ignored) { |
| 271 | return recurse(node, ignored); | 272 | return recurse(node, ignored); |
| 272 | } | 273 | } |
| 273 | 274 | ||
| 274 | @Override | 275 | @Override |
| 275 | public Void visitText(TextNode node, Void ignored) { | 276 | public Void visitText(TextNode node, Void ignored) { |
| 276 | return recurse(node, ignored); | 277 | return recurse(node, ignored); |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | @Override | 280 | @Override |
| 280 | public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { | 281 | public Void visitImportDeclaration(ImportDeclaration node, Void ignored) { |
| 281 | return recurse(node, ignored); | 282 | return recurse(node, ignored); |
| 282 | } | 283 | } |
| 283 | 284 | ||
| 284 | @Override | 285 | @Override |
| 285 | public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { | 286 | public Void visitInitializerBlock(InstanceInitializer node, Void ignored) { |
| 286 | return recurse(node, ignored); | 287 | return recurse(node, ignored); |
| 287 | } | 288 | } |
| 288 | 289 | ||
| 289 | @Override | 290 | @Override |
| 290 | public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { | 291 | public Void visitTypeParameterDeclaration(TypeParameterDeclaration node, Void ignored) { |
| 291 | return recurse(node, ignored); | 292 | return recurse(node, ignored); |
| 292 | } | 293 | } |
| 293 | 294 | ||
| 294 | @Override | 295 | @Override |
| 295 | public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { | 296 | public Void visitPackageDeclaration(PackageDeclaration node, Void ignored) { |
| 296 | return recurse(node, ignored); | 297 | return recurse(node, ignored); |
| 297 | } | 298 | } |
| 298 | 299 | ||
| 299 | @Override | 300 | @Override |
| 300 | public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { | 301 | public Void visitArraySpecifier(ArraySpecifier node, Void ignored) { |
| 301 | return recurse(node, ignored); | 302 | return recurse(node, ignored); |
| 302 | } | 303 | } |
| 303 | 304 | ||
| 304 | @Override | 305 | @Override |
| 305 | public Void visitComposedType(ComposedType node, Void ignored) { | 306 | public Void visitComposedType(ComposedType node, Void ignored) { |
| 306 | return recurse(node, ignored); | 307 | return recurse(node, ignored); |
| 307 | } | 308 | } |
| 308 | 309 | ||
| 309 | @Override | 310 | @Override |
| 310 | public Void visitWhileStatement(WhileStatement node, Void ignored) { | 311 | public Void visitWhileStatement(WhileStatement node, Void ignored) { |
| 311 | return recurse(node, ignored); | 312 | return recurse(node, ignored); |
| 312 | } | 313 | } |
| 313 | 314 | ||
| 314 | @Override | 315 | @Override |
| 315 | public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { | 316 | public Void visitPrimitiveExpression(PrimitiveExpression node, Void ignored) { |
| 316 | return recurse(node, ignored); | 317 | return recurse(node, ignored); |
| 317 | } | 318 | } |
| 318 | 319 | ||
| 319 | @Override | 320 | @Override |
| 320 | public Void visitCastExpression(CastExpression node, Void ignored) { | 321 | public Void visitCastExpression(CastExpression node, Void ignored) { |
| 321 | return recurse(node, ignored); | 322 | return recurse(node, ignored); |
| 322 | } | 323 | } |
| 323 | 324 | ||
| 324 | @Override | 325 | @Override |
| 325 | public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { | 326 | public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void ignored) { |
| 326 | return recurse(node, ignored); | 327 | return recurse(node, ignored); |
| 327 | } | 328 | } |
| 328 | 329 | ||
| 329 | @Override | 330 | @Override |
| 330 | public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { | 331 | public Void visitInstanceOfExpression(InstanceOfExpression node, Void ignored) { |
| 331 | return recurse(node, ignored); | 332 | return recurse(node, ignored); |
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | @Override | 335 | @Override |
| 335 | public Void visitIndexerExpression(IndexerExpression node, Void ignored) { | 336 | public Void visitIndexerExpression(IndexerExpression node, Void ignored) { |
| 336 | return recurse(node, ignored); | 337 | return recurse(node, ignored); |
| 337 | } | 338 | } |
| 338 | 339 | ||
| 339 | @Override | 340 | @Override |
| 340 | public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { | 341 | public Void visitIdentifierExpression(IdentifierExpression node, Void ignored) { |
| 341 | return recurse(node, ignored); | 342 | return recurse(node, ignored); |
| 342 | } | 343 | } |
| 343 | 344 | ||
| 344 | @Override | 345 | @Override |
| 345 | public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { | 346 | public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void ignored) { |
| 346 | return recurse(node, ignored); | 347 | return recurse(node, ignored); |
| 347 | } | 348 | } |
| 348 | 349 | ||
| 349 | @Override | 350 | @Override |
| 350 | public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { | 351 | public Void visitConditionalExpression(ConditionalExpression node, Void ignored) { |
| 351 | return recurse(node, ignored); | 352 | return recurse(node, ignored); |
| 352 | } | 353 | } |
| 353 | 354 | ||
| 354 | @Override | 355 | @Override |
| 355 | public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { | 356 | public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void ignored) { |
| 356 | return recurse(node, ignored); | 357 | return recurse(node, ignored); |
| 357 | } | 358 | } |
| 358 | 359 | ||
| 359 | @Override | 360 | @Override |
| 360 | public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { | 361 | public Void visitObjectCreationExpression(ObjectCreationExpression node, Void ignored) { |
| 361 | return recurse(node, ignored); | 362 | return recurse(node, ignored); |
| 362 | } | 363 | } |
| 363 | 364 | ||
| 364 | @Override | 365 | @Override |
| 365 | public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { | 366 | public Void visitArrayCreationExpression(ArrayCreationExpression node, Void ignored) { |
| 366 | return recurse(node, ignored); | 367 | return recurse(node, ignored); |
| 367 | } | 368 | } |
| 368 | 369 | ||
| 369 | @Override | 370 | @Override |
| 370 | public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { | 371 | public Void visitAssignmentExpression(AssignmentExpression node, Void ignored) { |
| 371 | return recurse(node, ignored); | 372 | return recurse(node, ignored); |
| 372 | } | 373 | } |
| 373 | 374 | ||
| 374 | @Override | 375 | @Override |
| 375 | public Void visitForStatement(ForStatement node, Void ignored) { | 376 | public Void visitForStatement(ForStatement node, Void ignored) { |
| 376 | return recurse(node, ignored); | 377 | return recurse(node, ignored); |
| 377 | } | 378 | } |
| 378 | 379 | ||
| 379 | @Override | 380 | @Override |
| 380 | public Void visitForEachStatement(ForEachStatement node, Void ignored) { | 381 | public Void visitForEachStatement(ForEachStatement node, Void ignored) { |
| 381 | return recurse(node, ignored); | 382 | return recurse(node, ignored); |
| 382 | } | 383 | } |
| 383 | 384 | ||
| 384 | @Override | 385 | @Override |
| 385 | public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { | 386 | public Void visitTryCatchStatement(TryCatchStatement node, Void ignored) { |
| 386 | return recurse(node, ignored); | 387 | return recurse(node, ignored); |
| 387 | } | 388 | } |
| 388 | 389 | ||
| 389 | @Override | 390 | @Override |
| 390 | public Void visitGotoStatement(GotoStatement node, Void ignored) { | 391 | public Void visitGotoStatement(GotoStatement node, Void ignored) { |
| 391 | return recurse(node, ignored); | 392 | return recurse(node, ignored); |
| 392 | } | 393 | } |
| 393 | 394 | ||
| 394 | @Override | 395 | @Override |
| 395 | public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { | 396 | public Void visitParenthesizedExpression(ParenthesizedExpression node, Void ignored) { |
| 396 | return recurse(node, ignored); | 397 | return recurse(node, ignored); |
| 397 | } | 398 | } |
| 398 | 399 | ||
| 399 | @Override | 400 | @Override |
| 400 | public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { | 401 | public Void visitSynchronizedStatement(SynchronizedStatement node, Void ignored) { |
| 401 | return recurse(node, ignored); | 402 | return recurse(node, ignored); |
| 402 | } | 403 | } |
| 403 | 404 | ||
| 404 | @Override | 405 | @Override |
| 405 | public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { | 406 | public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void ignored) { |
| 406 | return recurse(node, ignored); | 407 | return recurse(node, ignored); |
| 407 | } | 408 | } |
| 408 | 409 | ||
| 409 | @Override | 410 | @Override |
| 410 | public Void visitWildcardType(WildcardType node, Void ignored) { | 411 | public Void visitWildcardType(WildcardType node, Void ignored) { |
| 411 | return recurse(node, ignored); | 412 | return recurse(node, ignored); |
| 412 | } | 413 | } |
| 413 | 414 | ||
| 414 | @Override | 415 | @Override |
| 415 | public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { | 416 | public Void visitMethodGroupExpression(MethodGroupExpression node, Void ignored) { |
| 416 | return recurse(node, ignored); | 417 | return recurse(node, ignored); |
| 417 | } | 418 | } |
| 418 | 419 | ||
| 419 | @Override | 420 | @Override |
| 420 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { | 421 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, Void ignored) { |
| 421 | return recurse(node, ignored); | 422 | return recurse(node, ignored); |
| 422 | } | 423 | } |
| 423 | 424 | ||
| 424 | @Override | 425 | @Override |
| 425 | public Void visitAssertStatement(AssertStatement node, Void ignored) { | 426 | public Void visitAssertStatement(AssertStatement node, Void ignored) { |
| 426 | return recurse(node, ignored); | 427 | return recurse(node, ignored); |
| 427 | } | 428 | } |
| 428 | 429 | ||
| 429 | @Override | 430 | @Override |
| 430 | public Void visitLambdaExpression(LambdaExpression node, Void ignored) { | 431 | public Void visitLambdaExpression(LambdaExpression node, Void ignored) { |
| 431 | return recurse(node, ignored); | 432 | return recurse(node, ignored); |
| 432 | } | 433 | } |
| 433 | 434 | ||
| 434 | @Override | 435 | @Override |
| 435 | public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { | 436 | public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void ignored) { |
| 436 | return recurse(node, ignored); | 437 | return recurse(node, ignored); |
| 437 | } | 438 | } |
| 438 | } | 439 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java index ad5bab0c..6ec576e0 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import javassist.CtBehavior; | 14 | import javassist.CtBehavior; |
| @@ -16,36 +17,35 @@ import javassist.CtField; | |||
| 16 | import javassist.bytecode.AccessFlag; | 17 | import javassist.bytecode.AccessFlag; |
| 17 | import javassist.bytecode.InnerClassesAttribute; | 18 | import javassist.bytecode.InnerClassesAttribute; |
| 18 | 19 | ||
| 19 | |||
| 20 | public class ClassProtectifier { | 20 | public class ClassProtectifier { |
| 21 | 21 | ||
| 22 | public static CtClass protectify(CtClass c) { | 22 | public static CtClass protectify(CtClass c) { |
| 23 | 23 | ||
| 24 | // protectify all the fields | 24 | // protectify all the fields |
| 25 | for (CtField field : c.getDeclaredFields()) { | 25 | for (CtField field : c.getDeclaredFields()) { |
| 26 | field.setModifiers(protectify(field.getModifiers())); | 26 | field.setModifiers(protectify(field.getModifiers())); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | // protectify all the methods and constructors | 29 | // protectify all the methods and constructors |
| 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 31 | behavior.setModifiers(protectify(behavior.getModifiers())); | 31 | behavior.setModifiers(protectify(behavior.getModifiers())); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | // protectify all the inner classes | 34 | // protectify all the inner classes |
| 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 36 | if (attr != null) { | 36 | if (attr != null) { |
| 37 | for (int i = 0; i < attr.tableLength(); i++) { | 37 | for (int i = 0; i < attr.tableLength(); i++) { |
| 38 | attr.setAccessFlags(i, protectify(attr.accessFlags(i))); | 38 | attr.setAccessFlags(i, protectify(attr.accessFlags(i))); |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | return c; | 42 | return c; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | private static int protectify(int flags) { | 45 | private static int protectify(int flags) { |
| 46 | if (AccessFlag.isPrivate(flags)) { | 46 | if (AccessFlag.isPrivate(flags)) { |
| 47 | flags = AccessFlag.setProtected(flags); | 47 | flags = AccessFlag.setProtected(flags); |
| 48 | } | 48 | } |
| 49 | return flags; | 49 | return flags; |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java index 0aa7fac7..d627fe91 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import javassist.CtBehavior; | 14 | import javassist.CtBehavior; |
| @@ -16,36 +17,35 @@ import javassist.CtField; | |||
| 16 | import javassist.bytecode.AccessFlag; | 17 | import javassist.bytecode.AccessFlag; |
| 17 | import javassist.bytecode.InnerClassesAttribute; | 18 | import javassist.bytecode.InnerClassesAttribute; |
| 18 | 19 | ||
| 19 | |||
| 20 | public class ClassPublifier { | 20 | public class ClassPublifier { |
| 21 | 21 | ||
| 22 | public static CtClass publify(CtClass c) { | 22 | public static CtClass publify(CtClass c) { |
| 23 | 23 | ||
| 24 | // publify all the fields | 24 | // publify all the fields |
| 25 | for (CtField field : c.getDeclaredFields()) { | 25 | for (CtField field : c.getDeclaredFields()) { |
| 26 | field.setModifiers(publify(field.getModifiers())); | 26 | field.setModifiers(publify(field.getModifiers())); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | // publify all the methods and constructors | 29 | // publify all the methods and constructors |
| 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 31 | behavior.setModifiers(publify(behavior.getModifiers())); | 31 | behavior.setModifiers(publify(behavior.getModifiers())); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | // publify all the inner classes | 34 | // publify all the inner classes |
| 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 36 | if (attr != null) { | 36 | if (attr != null) { |
| 37 | for (int i = 0; i < attr.tableLength(); i++) { | 37 | for (int i = 0; i < attr.tableLength(); i++) { |
| 38 | attr.setAccessFlags(i, publify(attr.accessFlags(i))); | 38 | attr.setAccessFlags(i, publify(attr.accessFlags(i))); |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | return c; | 42 | return c; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | private static int publify(int flags) { | 45 | private static int publify(int flags) { |
| 46 | if (!AccessFlag.isPublic(flags)) { | 46 | if (!AccessFlag.isPublic(flags)) { |
| 47 | flags = AccessFlag.setPublic(flags); | 47 | flags = AccessFlag.setPublic(flags); |
| 48 | } | 48 | } |
| 49 | return flags; | 49 | return flags; |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java index d874633d..a52cab6d 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java | |||
| @@ -8,532 +8,532 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | 11 | ||
| 13 | import java.lang.reflect.InvocationTargetException; | 12 | package cuchaz.enigma.bytecode; |
| 14 | import java.lang.reflect.Method; | ||
| 15 | import java.util.Arrays; | ||
| 16 | import java.util.HashMap; | ||
| 17 | import java.util.List; | ||
| 18 | import java.util.Map; | ||
| 19 | 13 | ||
| 20 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 21 | import cuchaz.enigma.mapping.ClassNameReplacer; | 15 | import cuchaz.enigma.mapping.ClassNameReplacer; |
| 22 | import cuchaz.enigma.mapping.Mappings; | 16 | import cuchaz.enigma.mapping.Mappings; |
| 23 | import cuchaz.enigma.mapping.Translator; | 17 | import cuchaz.enigma.mapping.Translator; |
| 24 | import javassist.*; | 18 | import javassist.CtBehavior; |
| 19 | import javassist.CtClass; | ||
| 20 | import javassist.CtField; | ||
| 21 | import javassist.Modifier; | ||
| 25 | import javassist.bytecode.*; | 22 | import javassist.bytecode.*; |
| 26 | import javassist.bytecode.SignatureAttribute.*; | 23 | import javassist.bytecode.SignatureAttribute.*; |
| 27 | 24 | ||
| 25 | import java.lang.reflect.InvocationTargetException; | ||
| 26 | import java.lang.reflect.Method; | ||
| 27 | import java.util.Arrays; | ||
| 28 | import java.util.HashMap; | ||
| 29 | import java.util.List; | ||
| 30 | import java.util.Map; | ||
| 31 | |||
| 28 | public class ClassRenamer { | 32 | public class ClassRenamer { |
| 29 | 33 | ||
| 30 | private enum SignatureType { | 34 | public static void applyModifier(Object obj, Mappings.EntryModifier modifier) { |
| 31 | Class { | 35 | int mod = -1; |
| 32 | @Override | 36 | if (obj instanceof CtField) |
| 33 | public String rename(String signature, ReplacerClassMap map) { | 37 | mod = ((CtField) obj).getModifiers(); |
| 34 | return renameClassSignature(signature, map); | 38 | else if (obj instanceof CtBehavior) |
| 35 | } | 39 | mod = ((CtBehavior) obj).getModifiers(); |
| 36 | }, | 40 | else if (obj instanceof CtClass) |
| 37 | Field { | 41 | mod = ((CtClass) obj).getModifiers(); |
| 38 | @Override | 42 | |
| 39 | public String rename(String signature, ReplacerClassMap map) { | 43 | if (mod != -1) { |
| 40 | return renameFieldSignature(signature, map); | 44 | switch (modifier) { |
| 41 | } | 45 | case PRIVATE: |
| 42 | }, | 46 | mod = Modifier.setPrivate(mod); |
| 43 | Method { | 47 | break; |
| 44 | @Override | 48 | case PROTECTED: |
| 45 | public String rename(String signature, ReplacerClassMap map) { | 49 | mod = Modifier.setProtected(mod); |
| 46 | return renameMethodSignature(signature, map); | 50 | break; |
| 47 | } | 51 | case PUBLIC: |
| 48 | }; | 52 | mod = Modifier.setPublic(mod); |
| 49 | 53 | break; | |
| 50 | public abstract String rename(String signature, ReplacerClassMap map); | 54 | default: |
| 51 | } | 55 | break; |
| 52 | 56 | } | |
| 53 | private static class ReplacerClassMap extends HashMap<String, String> { | 57 | if (obj instanceof CtField) |
| 54 | 58 | ((CtField) obj).setModifiers(mod); | |
| 55 | private ClassNameReplacer replacer; | 59 | else if (obj instanceof CtBehavior) |
| 56 | 60 | ((CtBehavior) obj).setModifiers(mod); | |
| 57 | public ReplacerClassMap(ClassNameReplacer replacer) { | 61 | else |
| 58 | this.replacer = replacer; | 62 | ((CtClass) obj).setModifiers(mod); |
| 59 | } | 63 | } |
| 60 | 64 | } | |
| 61 | @Override | 65 | |
| 62 | public String get(Object obj) { | 66 | public static void renameClasses(CtClass c, final Translator translator) { |
| 63 | if (obj instanceof String) { | 67 | renameClasses(c, className -> { |
| 64 | return get((String) obj); | 68 | ClassEntry entry = translator.translateEntry(new ClassEntry(className)); |
| 65 | } | 69 | if (entry != null) { |
| 66 | return null; | 70 | return entry.getName(); |
| 67 | } | 71 | } |
| 68 | 72 | return null; | |
| 69 | public String get(String className) { | 73 | }); |
| 70 | return replacer.replace(className); | 74 | } |
| 71 | } | 75 | |
| 72 | } | 76 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { |
| 73 | 77 | renameClasses(c, className -> { | |
| 74 | public static void applyModifier(Object obj, Mappings.EntryModifier modifier) | 78 | ClassEntry entry = new ClassEntry(className); |
| 75 | { | 79 | if (entry.isInDefaultPackage()) { |
| 76 | int mod = -1; | 80 | return newPackageName + "/" + entry.getName(); |
| 77 | if (obj instanceof CtField) | 81 | } |
| 78 | mod = ((CtField) obj).getModifiers(); | 82 | return null; |
| 79 | else if (obj instanceof CtBehavior) | 83 | }); |
| 80 | mod = ((CtBehavior) obj).getModifiers(); | 84 | } |
| 81 | else if (obj instanceof CtClass) | 85 | |
| 82 | mod = ((CtClass) obj).getModifiers(); | 86 | public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { |
| 83 | 87 | renameClasses(c, className -> { | |
| 84 | if (mod != -1) | 88 | ClassEntry entry = new ClassEntry(className); |
| 85 | { | 89 | if (entry.getPackageName().equals(oldPackageName)) { |
| 86 | switch (modifier) | 90 | return entry.getSimpleName(); |
| 87 | { | 91 | } |
| 88 | case PRIVATE: | 92 | return null; |
| 89 | mod = Modifier.setPrivate(mod); | 93 | }); |
| 90 | break; | 94 | } |
| 91 | case PROTECTED: | 95 | |
| 92 | mod = Modifier.setProtected(mod); | 96 | @SuppressWarnings("unchecked") |
| 93 | break; | 97 | public static void renameClasses(CtClass c, ClassNameReplacer replacer) { |
| 94 | case PUBLIC: | 98 | |
| 95 | mod = Modifier.setPublic(mod); | 99 | // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =( |
| 96 | break; | 100 | |
| 97 | default: | 101 | ReplacerClassMap map = new ReplacerClassMap(replacer); |
| 98 | break; | 102 | ClassFile classFile = c.getClassFile(); |
| 99 | } | 103 | |
| 100 | if (obj instanceof CtField) | 104 | // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) |
| 101 | ((CtField) obj).setModifiers(mod); | 105 | ConstPool constPool = c.getClassFile().getConstPool(); |
| 102 | else if (obj instanceof CtBehavior) | 106 | constPool.renameClass(map); |
| 103 | ((CtBehavior) obj).setModifiers(mod); | 107 | |
| 104 | else | 108 | // rename class attributes |
| 105 | ((CtClass) obj).setModifiers(mod); | 109 | renameAttributes(classFile.getAttributes(), map, SignatureType.Class); |
| 106 | } | 110 | |
| 107 | } | 111 | // rename methods |
| 108 | 112 | for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) { | |
| 109 | public static void renameClasses(CtClass c, final Translator translator) { | 113 | methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map)); |
| 110 | renameClasses(c, className -> { | 114 | renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method); |
| 111 | ClassEntry entry = translator.translateEntry(new ClassEntry(className)); | 115 | } |
| 112 | if (entry != null) { | 116 | |
| 113 | return entry.getName(); | 117 | // rename fields |
| 114 | } | 118 | for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) { |
| 115 | return null; | 119 | fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map)); |
| 116 | }); | 120 | renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field); |
| 117 | } | 121 | } |
| 118 | 122 | ||
| 119 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { | 123 | // rename the class name itself last |
| 120 | renameClasses(c, className -> { | 124 | // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() |
| 121 | ClassEntry entry = new ClassEntry(className); | 125 | // we only want to replace exactly this class name |
| 122 | if (entry.isInDefaultPackage()) { | 126 | String newName = renameClassName(c.getName(), map); |
| 123 | return newPackageName + "/" + entry.getName(); | 127 | if (newName != null) { |
| 124 | } | 128 | c.setName(newName); |
| 125 | return null; | 129 | } |
| 126 | }); | 130 | |
| 127 | } | 131 | // replace simple names in the InnerClasses attribute too |
| 128 | 132 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | |
| 129 | public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { | 133 | if (attr != null) { |
| 130 | renameClasses(c, className -> { | 134 | for (int i = 0; i < attr.tableLength(); i++) { |
| 131 | ClassEntry entry = new ClassEntry(className); | 135 | |
| 132 | if (entry.getPackageName().equals(oldPackageName)) { | 136 | String innerName = attr.innerClass(i); |
| 133 | return entry.getSimpleName(); | 137 | // get the inner class full name (which has already been translated) |
| 134 | } | 138 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); |
| 135 | return null; | 139 | |
| 136 | }); | 140 | if (attr.innerNameIndex(i) != 0) { |
| 137 | } | 141 | // update the inner name |
| 138 | 142 | attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName())); | |
| 139 | @SuppressWarnings("unchecked") | 143 | } |
| 140 | public static void renameClasses(CtClass c, ClassNameReplacer replacer) { | ||
| 141 | |||
| 142 | // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =( | ||
| 143 | |||
| 144 | ReplacerClassMap map = new ReplacerClassMap(replacer); | ||
| 145 | ClassFile classFile = c.getClassFile(); | ||
| 146 | |||
| 147 | // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) | ||
| 148 | ConstPool constPool = c.getClassFile().getConstPool(); | ||
| 149 | constPool.renameClass(map); | ||
| 150 | |||
| 151 | // rename class attributes | ||
| 152 | renameAttributes(classFile.getAttributes(), map, SignatureType.Class); | ||
| 153 | |||
| 154 | // rename methods | ||
| 155 | for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) { | ||
| 156 | methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map)); | ||
| 157 | renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method); | ||
| 158 | } | ||
| 159 | |||
| 160 | // rename fields | ||
| 161 | for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) { | ||
| 162 | fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map)); | ||
| 163 | renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field); | ||
| 164 | } | ||
| 165 | |||
| 166 | // rename the class name itself last | ||
| 167 | // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() | ||
| 168 | // we only want to replace exactly this class name | ||
| 169 | String newName = renameClassName(c.getName(), map); | ||
| 170 | if (newName != null) { | ||
| 171 | c.setName(newName); | ||
| 172 | } | ||
| 173 | |||
| 174 | // replace simple names in the InnerClasses attribute too | ||
| 175 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 176 | if (attr != null) { | ||
| 177 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 178 | |||
| 179 | String innerName = attr.innerClass(i); | ||
| 180 | // get the inner class full name (which has already been translated) | ||
| 181 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); | ||
| 182 | |||
| 183 | if (attr.innerNameIndex(i) != 0) { | ||
| 184 | // update the inner name | ||
| 185 | attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 186 | } | ||
| 187 | 144 | ||
| 188 | /* DEBUG | 145 | /* DEBUG |
| 189 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | 146 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); |
| 190 | */ | 147 | */ |
| 191 | } | 148 | } |
| 192 | } | 149 | } |
| 193 | } | 150 | } |
| 194 | 151 | ||
| 195 | @SuppressWarnings("unchecked") | 152 | @SuppressWarnings("unchecked") |
| 196 | private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) { | 153 | private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) { |
| 197 | try { | 154 | try { |
| 198 | 155 | ||
| 199 | // make the rename class method accessible | 156 | // make the rename class method accessible |
| 200 | Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); | 157 | Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); |
| 201 | renameClassMethod.setAccessible(true); | 158 | renameClassMethod.setAccessible(true); |
| 202 | 159 | ||
| 203 | for (AttributeInfo attribute : attributes) { | 160 | for (AttributeInfo attribute : attributes) { |
| 204 | if (attribute instanceof SignatureAttribute) { | 161 | if (attribute instanceof SignatureAttribute) { |
| 205 | // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell | 162 | // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell |
| 206 | SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; | 163 | SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; |
| 207 | String newSignature = type.rename(signatureAttribute.getSignature(), map); | 164 | String newSignature = type.rename(signatureAttribute.getSignature(), map); |
| 208 | if (newSignature != null) { | 165 | if (newSignature != null) { |
| 209 | signatureAttribute.setSignature(newSignature); | 166 | signatureAttribute.setSignature(newSignature); |
| 210 | } | 167 | } |
| 211 | } else if (attribute instanceof CodeAttribute) { | 168 | } else if (attribute instanceof CodeAttribute) { |
| 212 | // code attributes have signature attributes too (indirectly) | 169 | // code attributes have signature attributes too (indirectly) |
| 213 | CodeAttribute codeAttribute = (CodeAttribute) attribute; | 170 | CodeAttribute codeAttribute = (CodeAttribute) attribute; |
| 214 | renameAttributes(codeAttribute.getAttributes(), map, type); | 171 | renameAttributes(codeAttribute.getAttributes(), map, type); |
| 215 | } else if (attribute instanceof LocalVariableTypeAttribute) { | 172 | } else if (attribute instanceof LocalVariableTypeAttribute) { |
| 216 | // lvt attributes have signature attributes too | 173 | // lvt attributes have signature attributes too |
| 217 | LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; | 174 | LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; |
| 218 | renameLocalVariableTypeAttribute(localVariableAttribute, map); | 175 | renameLocalVariableTypeAttribute(localVariableAttribute, map); |
| 219 | } else { | 176 | } else { |
| 220 | renameClassMethod.invoke(attribute, map); | 177 | renameClassMethod.invoke(attribute, map); |
| 221 | } | 178 | } |
| 222 | } | 179 | } |
| 223 | 180 | ||
| 224 | } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { | 181 | } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { |
| 225 | throw new Error("Unable to call javassist methods by reflection!", ex); | 182 | throw new Error("Unable to call javassist methods by reflection!", ex); |
| 226 | } | 183 | } |
| 227 | } | 184 | } |
| 228 | 185 | ||
| 229 | private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { | 186 | private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { |
| 230 | 187 | ||
| 231 | // adapted from LocalVariableAttribute.renameClass() | 188 | // adapted from LocalVariableAttribute.renameClass() |
| 232 | ConstPool cp = attribute.getConstPool(); | 189 | ConstPool cp = attribute.getConstPool(); |
| 233 | int n = attribute.tableLength(); | 190 | int n = attribute.tableLength(); |
| 234 | byte[] info = attribute.get(); | 191 | byte[] info = attribute.get(); |
| 235 | for (int i = 0; i < n; ++i) { | 192 | for (int i = 0; i < n; ++i) { |
| 236 | int pos = i * 10 + 2; | 193 | int pos = i * 10 + 2; |
| 237 | int index = ByteArray.readU16bit(info, pos + 6); | 194 | int index = ByteArray.readU16bit(info, pos + 6); |
| 238 | if (index != 0) { | 195 | if (index != 0) { |
| 239 | String signature = cp.getUtf8Info(index); | 196 | String signature = cp.getUtf8Info(index); |
| 240 | String newSignature = renameLocalVariableSignature(signature, map); | 197 | String newSignature = renameLocalVariableSignature(signature, map); |
| 241 | if (newSignature != null) { | 198 | if (newSignature != null) { |
| 242 | ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); | 199 | ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); |
| 243 | } | 200 | } |
| 244 | } | 201 | } |
| 245 | } | 202 | } |
| 246 | } | 203 | } |
| 247 | 204 | ||
| 248 | private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { | 205 | private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { |
| 249 | 206 | ||
| 250 | // for some reason, signatures with . in them don't count as field signatures | 207 | // for some reason, signatures with . in them don't count as field signatures |
| 251 | // looks like anonymous classes delimit with . in stead of $ | 208 | // looks like anonymous classes delimit with . in stead of $ |
| 252 | // convert the . to $, but keep track of how many we replace | 209 | // convert the . to $, but keep track of how many we replace |
| 253 | // we need to put them back after we translate | 210 | // we need to put them back after we translate |
| 254 | int start = signature.lastIndexOf('$') + 1; | 211 | int start = signature.lastIndexOf('$') + 1; |
| 255 | int numConverted = 0; | 212 | int numConverted = 0; |
| 256 | StringBuilder buf = new StringBuilder(signature); | 213 | StringBuilder buf = new StringBuilder(signature); |
| 257 | for (int i = buf.length() - 1; i >= start; i--) { | 214 | for (int i = buf.length() - 1; i >= start; i--) { |
| 258 | char c = buf.charAt(i); | 215 | char c = buf.charAt(i); |
| 259 | if (c == '.') { | 216 | if (c == '.') { |
| 260 | buf.setCharAt(i, '$'); | 217 | buf.setCharAt(i, '$'); |
| 261 | numConverted++; | 218 | numConverted++; |
| 262 | } | 219 | } |
| 263 | } | 220 | } |
| 264 | signature = buf.toString(); | 221 | signature = buf.toString(); |
| 265 | 222 | ||
| 266 | // translate | 223 | // translate |
| 267 | String newSignature = renameFieldSignature(signature, map); | 224 | String newSignature = renameFieldSignature(signature, map); |
| 268 | if (newSignature != null) { | 225 | if (newSignature != null) { |
| 269 | 226 | ||
| 270 | // put the delimiters back | 227 | // put the delimiters back |
| 271 | buf = new StringBuilder(newSignature); | 228 | buf = new StringBuilder(newSignature); |
| 272 | for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { | 229 | for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { |
| 273 | char c = buf.charAt(i); | 230 | char c = buf.charAt(i); |
| 274 | if (c == '$') { | 231 | if (c == '$') { |
| 275 | buf.setCharAt(i, '.'); | 232 | buf.setCharAt(i, '.'); |
| 276 | numConverted--; | 233 | numConverted--; |
| 277 | } | 234 | } |
| 278 | } | 235 | } |
| 279 | assert (numConverted == 0); | 236 | assert (numConverted == 0); |
| 280 | newSignature = buf.toString(); | 237 | newSignature = buf.toString(); |
| 281 | 238 | ||
| 282 | return newSignature; | 239 | return newSignature; |
| 283 | } | 240 | } |
| 284 | 241 | ||
| 285 | return null; | 242 | return null; |
| 286 | } | 243 | } |
| 287 | 244 | ||
| 288 | private static String renameClassSignature(String signature, ReplacerClassMap map) { | 245 | private static String renameClassSignature(String signature, ReplacerClassMap map) { |
| 289 | try { | 246 | try { |
| 290 | ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); | 247 | ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); |
| 291 | return type.encode(); | 248 | return type.encode(); |
| 292 | } catch (BadBytecode ex) { | 249 | } catch (BadBytecode ex) { |
| 293 | throw new Error("Can't parse field signature: " + signature); | 250 | throw new Error("Can't parse field signature: " + signature); |
| 294 | } | 251 | } |
| 295 | } | 252 | } |
| 296 | 253 | ||
| 297 | private static String renameFieldSignature(String signature, ReplacerClassMap map) { | 254 | private static String renameFieldSignature(String signature, ReplacerClassMap map) { |
| 298 | try { | 255 | try { |
| 299 | ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); | 256 | ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); |
| 300 | if (type != null) { | 257 | if (type != null) { |
| 301 | return type.encode(); | 258 | return type.encode(); |
| 302 | } | 259 | } |
| 303 | return null; | 260 | return null; |
| 304 | } catch (BadBytecode ex) { | 261 | } catch (BadBytecode ex) { |
| 305 | throw new Error("Can't parse class signature: " + signature); | 262 | throw new Error("Can't parse class signature: " + signature); |
| 306 | } | 263 | } |
| 307 | } | 264 | } |
| 308 | 265 | ||
| 309 | private static String renameMethodSignature(String signature, ReplacerClassMap map) { | 266 | private static String renameMethodSignature(String signature, ReplacerClassMap map) { |
| 310 | try { | 267 | try { |
| 311 | MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); | 268 | MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); |
| 312 | return type.encode(); | 269 | return type.encode(); |
| 313 | } catch (BadBytecode ex) { | 270 | } catch (BadBytecode ex) { |
| 314 | throw new Error("Can't parse method signature: " + signature); | 271 | throw new Error("Can't parse method signature: " + signature); |
| 315 | } | 272 | } |
| 316 | } | 273 | } |
| 317 | 274 | ||
| 318 | private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) | 275 | private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) { |
| 319 | { | 276 | if (typeParamTypes != null) { |
| 320 | if (typeParamTypes != null) { | 277 | typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); |
| 321 | typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); | 278 | for (int i = 0; i < typeParamTypes.length; i++) { |
| 322 | for (int i = 0; i < typeParamTypes.length; i++) { | 279 | TypeParameter newParamType = renameType(typeParamTypes[i], map); |
| 323 | TypeParameter newParamType = renameType(typeParamTypes[i], map); | 280 | if (newParamType != null) { |
| 324 | if (newParamType != null) { | 281 | typeParamTypes[i] = newParamType; |
| 325 | typeParamTypes[i] = newParamType; | 282 | } |
| 326 | } | 283 | } |
| 327 | } | 284 | } |
| 328 | } | 285 | return typeParamTypes; |
| 329 | return typeParamTypes; | 286 | } |
| 330 | } | 287 | |
| 331 | 288 | private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { | |
| 332 | private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { | 289 | |
| 333 | 290 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); | |
| 334 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); | 291 | |
| 335 | 292 | ClassType superclassType = type.getSuperClass(); | |
| 336 | ClassType superclassType = type.getSuperClass(); | 293 | if (superclassType != ClassType.OBJECT) { |
| 337 | if (superclassType != ClassType.OBJECT) { | 294 | ClassType newSuperclassType = renameType(superclassType, map); |
| 338 | ClassType newSuperclassType = renameType(superclassType, map); | 295 | if (newSuperclassType != null) { |
| 339 | if (newSuperclassType != null) { | 296 | superclassType = newSuperclassType; |
| 340 | superclassType = newSuperclassType; | 297 | } |
| 341 | } | 298 | } |
| 342 | } | 299 | |
| 343 | 300 | ClassType[] interfaceTypes = type.getInterfaces(); | |
| 344 | ClassType[] interfaceTypes = type.getInterfaces(); | 301 | if (interfaceTypes != null) { |
| 345 | if (interfaceTypes != null) { | 302 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); |
| 346 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); | 303 | for (int i = 0; i < interfaceTypes.length; i++) { |
| 347 | for (int i = 0; i < interfaceTypes.length; i++) { | 304 | ClassType newInterfaceType = renameType(interfaceTypes[i], map); |
| 348 | ClassType newInterfaceType = renameType(interfaceTypes[i], map); | 305 | if (newInterfaceType != null) { |
| 349 | if (newInterfaceType != null) { | 306 | interfaceTypes[i] = newInterfaceType; |
| 350 | interfaceTypes[i] = newInterfaceType; | 307 | } |
| 351 | } | 308 | } |
| 352 | } | 309 | } |
| 353 | } | 310 | |
| 354 | 311 | return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); | |
| 355 | return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); | 312 | } |
| 356 | } | 313 | |
| 357 | 314 | private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { | |
| 358 | private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { | 315 | |
| 359 | 316 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); | |
| 360 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); | 317 | |
| 361 | 318 | Type[] paramTypes = type.getParameterTypes(); | |
| 362 | Type[] paramTypes = type.getParameterTypes(); | 319 | if (paramTypes != null) { |
| 363 | if (paramTypes != null) { | 320 | paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); |
| 364 | paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); | 321 | for (int i = 0; i < paramTypes.length; i++) { |
| 365 | for (int i = 0; i < paramTypes.length; i++) { | 322 | Type newParamType = renameType(paramTypes[i], map); |
| 366 | Type newParamType = renameType(paramTypes[i], map); | 323 | if (newParamType != null) { |
| 367 | if (newParamType != null) { | 324 | paramTypes[i] = newParamType; |
| 368 | paramTypes[i] = newParamType; | 325 | } |
| 369 | } | 326 | } |
| 370 | } | 327 | } |
| 371 | } | 328 | |
| 372 | 329 | Type returnType = type.getReturnType(); | |
| 373 | Type returnType = type.getReturnType(); | 330 | if (returnType != null) { |
| 374 | if (returnType != null) { | 331 | Type newReturnType = renameType(returnType, map); |
| 375 | Type newReturnType = renameType(returnType, map); | 332 | if (newReturnType != null) { |
| 376 | if (newReturnType != null) { | 333 | returnType = newReturnType; |
| 377 | returnType = newReturnType; | 334 | } |
| 378 | } | 335 | } |
| 379 | } | 336 | |
| 380 | 337 | ObjectType[] exceptionTypes = type.getExceptionTypes(); | |
| 381 | ObjectType[] exceptionTypes = type.getExceptionTypes(); | 338 | if (exceptionTypes != null) { |
| 382 | if (exceptionTypes != null) { | 339 | exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); |
| 383 | exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); | 340 | for (int i = 0; i < exceptionTypes.length; i++) { |
| 384 | for (int i = 0; i < exceptionTypes.length; i++) { | 341 | ObjectType newExceptionType = renameType(exceptionTypes[i], map); |
| 385 | ObjectType newExceptionType = renameType(exceptionTypes[i], map); | 342 | if (newExceptionType != null) { |
| 386 | if (newExceptionType != null) { | 343 | exceptionTypes[i] = newExceptionType; |
| 387 | exceptionTypes[i] = newExceptionType; | 344 | } |
| 388 | } | 345 | } |
| 389 | } | 346 | } |
| 390 | } | 347 | |
| 391 | 348 | return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); | |
| 392 | return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); | 349 | } |
| 393 | } | 350 | |
| 394 | 351 | private static Type renameType(Type type, ReplacerClassMap map) { | |
| 395 | private static Type renameType(Type type, ReplacerClassMap map) { | 352 | if (type instanceof ObjectType) { |
| 396 | if (type instanceof ObjectType) { | 353 | return renameType((ObjectType) type, map); |
| 397 | return renameType((ObjectType) type, map); | 354 | } else if (type instanceof BaseType) { |
| 398 | } else if (type instanceof BaseType) { | 355 | return renameType((BaseType) type, map); |
| 399 | return renameType((BaseType) type, map); | 356 | } else { |
| 400 | } else { | 357 | throw new Error("Don't know how to rename type " + type.getClass()); |
| 401 | throw new Error("Don't know how to rename type " + type.getClass()); | 358 | } |
| 402 | } | 359 | } |
| 403 | } | 360 | |
| 404 | 361 | private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { | |
| 405 | private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { | 362 | if (type instanceof ArrayType) { |
| 406 | if (type instanceof ArrayType) { | 363 | return renameType((ArrayType) type, map); |
| 407 | return renameType((ArrayType) type, map); | 364 | } else if (type instanceof ClassType) { |
| 408 | } else if (type instanceof ClassType) { | 365 | return renameType((ClassType) type, map); |
| 409 | return renameType((ClassType) type, map); | 366 | } else if (type instanceof TypeVariable) { |
| 410 | } else if (type instanceof TypeVariable) { | 367 | return renameType((TypeVariable) type, map); |
| 411 | return renameType((TypeVariable) type, map); | 368 | } else { |
| 412 | } else { | 369 | throw new Error("Don't know how to rename type " + type.getClass()); |
| 413 | throw new Error("Don't know how to rename type " + type.getClass()); | 370 | } |
| 414 | } | 371 | } |
| 415 | } | 372 | |
| 416 | 373 | private static BaseType renameType(BaseType type, ReplacerClassMap map) { | |
| 417 | private static BaseType renameType(BaseType type, ReplacerClassMap map) { | 374 | // don't have to rename primitives |
| 418 | // don't have to rename primitives | 375 | return null; |
| 419 | return null; | 376 | } |
| 420 | } | 377 | |
| 421 | 378 | private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { | |
| 422 | private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { | 379 | // don't have to rename template args |
| 423 | // don't have to rename template args | 380 | return null; |
| 424 | return null; | 381 | } |
| 425 | } | 382 | |
| 426 | 383 | private static ClassType renameType(ClassType type, ReplacerClassMap map) { | |
| 427 | private static ClassType renameType(ClassType type, ReplacerClassMap map) { | 384 | |
| 428 | 385 | // translate type args | |
| 429 | // translate type args | 386 | TypeArgument[] args = type.getTypeArguments(); |
| 430 | TypeArgument[] args = type.getTypeArguments(); | 387 | if (args != null) { |
| 431 | if (args != null) { | 388 | args = Arrays.copyOf(args, args.length); |
| 432 | args = Arrays.copyOf(args, args.length); | 389 | for (int i = 0; i < args.length; i++) { |
| 433 | for (int i = 0; i < args.length; i++) { | 390 | TypeArgument newType = renameType(args[i], map); |
| 434 | TypeArgument newType = renameType(args[i], map); | 391 | if (newType != null) { |
| 435 | if (newType != null) { | 392 | args[i] = newType; |
| 436 | args[i] = newType; | 393 | } |
| 437 | } | 394 | } |
| 438 | } | 395 | } |
| 439 | } | 396 | |
| 440 | 397 | if (type instanceof NestedClassType) { | |
| 441 | if (type instanceof NestedClassType) { | 398 | NestedClassType nestedType = (NestedClassType) type; |
| 442 | NestedClassType nestedType = (NestedClassType) type; | 399 | |
| 443 | 400 | // translate the name | |
| 444 | // translate the name | 401 | String name = getClassName(type); |
| 445 | String name = getClassName(type); | 402 | String newName = map.get(name); |
| 446 | String newName = map.get(name); | 403 | if (newName != null) { |
| 447 | if (newName != null) { | 404 | name = new ClassEntry(newName).getInnermostClassName(); |
| 448 | name = new ClassEntry(newName).getInnermostClassName(); | 405 | } |
| 449 | } | 406 | |
| 450 | 407 | // translate the parent class too | |
| 451 | // translate the parent class too | 408 | ClassType parent = renameType(nestedType.getDeclaringClass(), map); |
| 452 | ClassType parent = renameType(nestedType.getDeclaringClass(), map); | 409 | if (parent == null) { |
| 453 | if (parent == null) { | 410 | parent = nestedType.getDeclaringClass(); |
| 454 | parent = nestedType.getDeclaringClass(); | 411 | } |
| 455 | } | 412 | |
| 456 | 413 | return new NestedClassType(parent, name, args); | |
| 457 | return new NestedClassType(parent, name, args); | 414 | } else { |
| 458 | } else { | 415 | |
| 459 | 416 | // translate the name | |
| 460 | // translate the name | 417 | String name = type.getName(); |
| 461 | String name = type.getName(); | 418 | String newName = renameClassName(name, map); |
| 462 | String newName = renameClassName(name, map); | 419 | if (newName != null) { |
| 463 | if (newName != null) { | 420 | name = newName; |
| 464 | name = newName; | 421 | } |
| 465 | } | 422 | |
| 466 | 423 | return new ClassType(name, args); | |
| 467 | return new ClassType(name, args); | 424 | } |
| 468 | } | 425 | } |
| 469 | } | 426 | |
| 470 | 427 | private static String getClassName(ClassType type) { | |
| 471 | private static String getClassName(ClassType type) { | 428 | if (type instanceof NestedClassType) { |
| 472 | if (type instanceof NestedClassType) { | 429 | NestedClassType nestedType = (NestedClassType) type; |
| 473 | NestedClassType nestedType = (NestedClassType) type; | 430 | return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); |
| 474 | return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); | 431 | } else { |
| 475 | } else { | 432 | return Descriptor.toJvmName(type.getName()); |
| 476 | return Descriptor.toJvmName(type.getName()); | 433 | } |
| 477 | } | 434 | } |
| 478 | } | 435 | |
| 479 | 436 | private static String renameClassName(String name, ReplacerClassMap map) { | |
| 480 | private static String renameClassName(String name, ReplacerClassMap map) { | 437 | String newName = map.get(Descriptor.toJvmName(name)); |
| 481 | String newName = map.get(Descriptor.toJvmName(name)); | 438 | if (newName != null) { |
| 482 | if (newName != null) { | 439 | return Descriptor.toJavaName(newName); |
| 483 | return Descriptor.toJavaName(newName); | 440 | } |
| 484 | } | 441 | return null; |
| 485 | return null; | 442 | } |
| 486 | } | 443 | |
| 487 | 444 | private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { | |
| 488 | private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { | 445 | ObjectType subType = type.getType(); |
| 489 | ObjectType subType = type.getType(); | 446 | if (subType != null) { |
| 490 | if (subType != null) { | 447 | ObjectType newSubType = renameType(subType, map); |
| 491 | ObjectType newSubType = renameType(subType, map); | 448 | if (newSubType != null) { |
| 492 | if (newSubType != null) { | 449 | switch (type.getKind()) { |
| 493 | switch (type.getKind()) { | 450 | case ' ': |
| 494 | case ' ': | 451 | return new TypeArgument(newSubType); |
| 495 | return new TypeArgument(newSubType); | 452 | case '+': |
| 496 | case '+': | 453 | return TypeArgument.subclassOf(newSubType); |
| 497 | return TypeArgument.subclassOf(newSubType); | 454 | case '-': |
| 498 | case '-': | 455 | return TypeArgument.superOf(newSubType); |
| 499 | return TypeArgument.superOf(newSubType); | 456 | default: |
| 500 | default: | 457 | throw new Error("Unknown type kind: " + type.getKind()); |
| 501 | throw new Error("Unknown type kind: " + type.getKind()); | 458 | } |
| 502 | } | 459 | } |
| 503 | } | 460 | } |
| 504 | } | 461 | return null; |
| 505 | return null; | 462 | } |
| 506 | } | 463 | |
| 507 | 464 | private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { | |
| 508 | private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { | 465 | Type newSubType = renameType(type.getComponentType(), map); |
| 509 | Type newSubType = renameType(type.getComponentType(), map); | 466 | if (newSubType != null) { |
| 510 | if (newSubType != null) { | 467 | return new ArrayType(type.getDimension(), newSubType); |
| 511 | return new ArrayType(type.getDimension(), newSubType); | 468 | } |
| 512 | } | 469 | return null; |
| 513 | return null; | 470 | } |
| 514 | } | 471 | |
| 515 | 472 | private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { | |
| 516 | private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { | 473 | |
| 517 | 474 | ObjectType superclassType = type.getClassBound(); | |
| 518 | ObjectType superclassType = type.getClassBound(); | 475 | if (superclassType != null) { |
| 519 | if (superclassType != null) { | 476 | ObjectType newSuperclassType = renameType(superclassType, map); |
| 520 | ObjectType newSuperclassType = renameType(superclassType, map); | 477 | if (newSuperclassType != null) { |
| 521 | if (newSuperclassType != null) { | 478 | superclassType = newSuperclassType; |
| 522 | superclassType = newSuperclassType; | 479 | } |
| 523 | } | 480 | } |
| 524 | } | 481 | |
| 525 | 482 | ObjectType[] interfaceTypes = type.getInterfaceBound(); | |
| 526 | ObjectType[] interfaceTypes = type.getInterfaceBound(); | 483 | if (interfaceTypes != null) { |
| 527 | if (interfaceTypes != null) { | 484 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); |
| 528 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); | 485 | for (int i = 0; i < interfaceTypes.length; i++) { |
| 529 | for (int i = 0; i < interfaceTypes.length; i++) { | 486 | ObjectType newInterfaceType = renameType(interfaceTypes[i], map); |
| 530 | ObjectType newInterfaceType = renameType(interfaceTypes[i], map); | 487 | if (newInterfaceType != null) { |
| 531 | if (newInterfaceType != null) { | 488 | interfaceTypes[i] = newInterfaceType; |
| 532 | interfaceTypes[i] = newInterfaceType; | 489 | } |
| 533 | } | 490 | } |
| 534 | } | 491 | } |
| 535 | } | 492 | |
| 536 | 493 | return new TypeParameter(type.getName(), superclassType, interfaceTypes); | |
| 537 | return new TypeParameter(type.getName(), superclassType, interfaceTypes); | 494 | } |
| 538 | } | 495 | |
| 496 | private enum SignatureType { | ||
| 497 | Class { | ||
| 498 | @Override | ||
| 499 | public String rename(String signature, ReplacerClassMap map) { | ||
| 500 | return renameClassSignature(signature, map); | ||
| 501 | } | ||
| 502 | }, | ||
| 503 | Field { | ||
| 504 | @Override | ||
| 505 | public String rename(String signature, ReplacerClassMap map) { | ||
| 506 | return renameFieldSignature(signature, map); | ||
| 507 | } | ||
| 508 | }, | ||
| 509 | Method { | ||
| 510 | @Override | ||
| 511 | public String rename(String signature, ReplacerClassMap map) { | ||
| 512 | return renameMethodSignature(signature, map); | ||
| 513 | } | ||
| 514 | }; | ||
| 515 | |||
| 516 | public abstract String rename(String signature, ReplacerClassMap map); | ||
| 517 | } | ||
| 518 | |||
| 519 | private static class ReplacerClassMap extends HashMap<String, String> { | ||
| 520 | |||
| 521 | private ClassNameReplacer replacer; | ||
| 522 | |||
| 523 | public ReplacerClassMap(ClassNameReplacer replacer) { | ||
| 524 | this.replacer = replacer; | ||
| 525 | } | ||
| 526 | |||
| 527 | @Override | ||
| 528 | public String get(Object obj) { | ||
| 529 | if (obj instanceof String) { | ||
| 530 | return get((String) obj); | ||
| 531 | } | ||
| 532 | return null; | ||
| 533 | } | ||
| 534 | |||
| 535 | public String get(String className) { | ||
| 536 | return replacer.replace(className); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | } | 539 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java index 62ebfafb..1ebf6561 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java | |||
| @@ -8,155 +8,158 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.*; | 14 | import cuchaz.enigma.mapping.*; |
| 14 | import cuchaz.enigma.mapping.Translator; | 15 | import javassist.CtBehavior; |
| 15 | import javassist.*; | 16 | import javassist.CtClass; |
| 17 | import javassist.CtField; | ||
| 18 | import javassist.CtMethod; | ||
| 16 | import javassist.bytecode.*; | 19 | import javassist.bytecode.*; |
| 17 | 20 | ||
| 18 | public class ClassTranslator { | 21 | public class ClassTranslator { |
| 19 | 22 | ||
| 20 | private Translator translator; | 23 | private Translator translator; |
| 21 | 24 | ||
| 22 | public ClassTranslator(Translator translator) { | 25 | public ClassTranslator(Translator translator) { |
| 23 | this.translator = translator; | 26 | this.translator = translator; |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | public void translate(CtClass c) { | 29 | public void translate(CtClass c) { |
| 27 | 30 | ||
| 28 | // NOTE: the order of these translations is very important | 31 | // NOTE: the order of these translations is very important |
| 29 | 32 | ||
| 30 | // translate all the field and method references in the code by editing the constant pool | 33 | // translate all the field and method references in the code by editing the constant pool |
| 31 | ConstPool constants = c.getClassFile().getConstPool(); | 34 | ConstPool constants = c.getClassFile().getConstPool(); |
| 32 | ConstPoolEditor editor = new ConstPoolEditor(constants); | 35 | ConstPoolEditor editor = new ConstPoolEditor(constants); |
| 33 | for (int i = 1; i < constants.getSize(); i++) { | 36 | for (int i = 1; i < constants.getSize(); i++) { |
| 34 | switch (constants.getTag(i)) { | 37 | switch (constants.getTag(i)) { |
| 35 | 38 | ||
| 36 | case ConstPool.CONST_Fieldref: { | 39 | case ConstPool.CONST_Fieldref: { |
| 37 | 40 | ||
| 38 | // translate the name and type | 41 | // translate the name and type |
| 39 | FieldEntry entry = EntryFactory.getFieldEntry( | 42 | FieldEntry entry = EntryFactory.getFieldEntry( |
| 40 | Descriptor.toJvmName(constants.getFieldrefClassName(i)), | 43 | Descriptor.toJvmName(constants.getFieldrefClassName(i)), |
| 41 | constants.getFieldrefName(i), | 44 | constants.getFieldrefName(i), |
| 42 | constants.getFieldrefType(i) | 45 | constants.getFieldrefType(i) |
| 43 | ); | 46 | ); |
| 44 | FieldEntry translatedEntry = this.translator.translateEntry(entry); | 47 | FieldEntry translatedEntry = this.translator.translateEntry(entry); |
| 45 | if (!entry.equals(translatedEntry)) { | 48 | if (!entry.equals(translatedEntry)) { |
| 46 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); | 49 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); |
| 47 | } | 50 | } |
| 48 | } | 51 | } |
| 49 | break; | 52 | break; |
| 50 | 53 | ||
| 51 | case ConstPool.CONST_Methodref: | 54 | case ConstPool.CONST_Methodref: |
| 52 | case ConstPool.CONST_InterfaceMethodref: { | 55 | case ConstPool.CONST_InterfaceMethodref: { |
| 53 | 56 | ||
| 54 | // translate the name and type (ie signature) | 57 | // translate the name and type (ie signature) |
| 55 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( | 58 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( |
| 56 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), | 59 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), |
| 57 | editor.getMemberrefName(i), | 60 | editor.getMemberrefName(i), |
| 58 | editor.getMemberrefType(i) | 61 | editor.getMemberrefType(i) |
| 59 | ); | 62 | ); |
| 60 | BehaviorEntry translatedEntry = this.translator.translateEntry(entry); | 63 | BehaviorEntry translatedEntry = this.translator.translateEntry(entry); |
| 61 | if (!entry.equals(translatedEntry)) { | 64 | if (!entry.equals(translatedEntry)) { |
| 62 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); | 65 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); |
| 63 | } | 66 | } |
| 64 | } | 67 | } |
| 65 | break; | 68 | break; |
| 66 | default: | 69 | default: |
| 67 | break; | 70 | break; |
| 68 | } | 71 | } |
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 74 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 72 | Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); | 75 | Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); |
| 73 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | 76 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) |
| 74 | ClassRenamer.applyModifier(c, modifier); | 77 | ClassRenamer.applyModifier(c, modifier); |
| 75 | 78 | ||
| 76 | // translate all the fields | 79 | // translate all the fields |
| 77 | for (CtField field : c.getDeclaredFields()) { | 80 | for (CtField field : c.getDeclaredFields()) { |
| 78 | 81 | ||
| 79 | // translate the name | 82 | // translate the name |
| 80 | FieldEntry entry = EntryFactory.getFieldEntry(field); | 83 | FieldEntry entry = EntryFactory.getFieldEntry(field); |
| 81 | String translatedName = this.translator.translate(entry); | 84 | String translatedName = this.translator.translate(entry); |
| 82 | modifier = this.translator.getModifier(entry); | 85 | modifier = this.translator.getModifier(entry); |
| 83 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | 86 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) |
| 84 | ClassRenamer.applyModifier(field, modifier); | 87 | ClassRenamer.applyModifier(field, modifier); |
| 85 | 88 | ||
| 86 | if (translatedName != null) { | 89 | if (translatedName != null) { |
| 87 | field.setName(translatedName); | 90 | field.setName(translatedName); |
| 88 | } | 91 | } |
| 89 | 92 | ||
| 90 | // translate the type | 93 | // translate the type |
| 91 | Type translatedType = this.translator.translateType(entry.getType()); | 94 | Type translatedType = this.translator.translateType(entry.getType()); |
| 92 | field.getFieldInfo().setDescriptor(translatedType.toString()); | 95 | field.getFieldInfo().setDescriptor(translatedType.toString()); |
| 93 | } | 96 | } |
| 94 | 97 | ||
| 95 | // translate all the methods and constructors | 98 | // translate all the methods and constructors |
| 96 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 99 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 97 | 100 | ||
| 98 | BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); | 101 | BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); |
| 99 | 102 | ||
| 100 | modifier = this.translator.getModifier(entry); | 103 | modifier = this.translator.getModifier(entry); |
| 101 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | 104 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) |
| 102 | ClassRenamer.applyModifier(behavior, modifier); | 105 | ClassRenamer.applyModifier(behavior, modifier); |
| 103 | 106 | ||
| 104 | if (behavior instanceof CtMethod) { | 107 | if (behavior instanceof CtMethod) { |
| 105 | CtMethod method = (CtMethod) behavior; | 108 | CtMethod method = (CtMethod) behavior; |
| 106 | 109 | ||
| 107 | // translate the name | 110 | // translate the name |
| 108 | String translatedName = this.translator.translate(entry); | 111 | String translatedName = this.translator.translate(entry); |
| 109 | if (translatedName != null) { | 112 | if (translatedName != null) { |
| 110 | method.setName(translatedName); | 113 | method.setName(translatedName); |
| 111 | } | 114 | } |
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | if (entry.getSignature() != null) { | 117 | if (entry.getSignature() != null) { |
| 115 | // translate the signature | 118 | // translate the signature |
| 116 | Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); | 119 | Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); |
| 117 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); | 120 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); |
| 118 | } | 121 | } |
| 119 | } | 122 | } |
| 120 | 123 | ||
| 121 | // translate the EnclosingMethod attribute | 124 | // translate the EnclosingMethod attribute |
| 122 | EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); | 125 | EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); |
| 123 | if (enclosingMethodAttr != null) { | 126 | if (enclosingMethodAttr != null) { |
| 124 | 127 | ||
| 125 | if (enclosingMethodAttr.methodIndex() == 0) { | 128 | if (enclosingMethodAttr.methodIndex() == 0) { |
| 126 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); | 129 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); |
| 127 | BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); | 130 | BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); |
| 128 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( | 131 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( |
| 129 | constants, | 132 | constants, |
| 130 | deobfBehaviorEntry.getClassName() | 133 | deobfBehaviorEntry.getClassName() |
| 131 | )); | 134 | )); |
| 132 | } else { | 135 | } else { |
| 133 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( | 136 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( |
| 134 | Descriptor.toJvmName(enclosingMethodAttr.className()), | 137 | Descriptor.toJvmName(enclosingMethodAttr.className()), |
| 135 | enclosingMethodAttr.methodName(), | 138 | enclosingMethodAttr.methodName(), |
| 136 | enclosingMethodAttr.methodDescriptor() | 139 | enclosingMethodAttr.methodDescriptor() |
| 137 | ); | 140 | ); |
| 138 | BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); | 141 | BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); |
| 139 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( | 142 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( |
| 140 | constants, | 143 | constants, |
| 141 | deobfBehaviorEntry.getClassName(), | 144 | deobfBehaviorEntry.getClassName(), |
| 142 | deobfBehaviorEntry.getName(), | 145 | deobfBehaviorEntry.getName(), |
| 143 | deobfBehaviorEntry.getSignature().toString() | 146 | deobfBehaviorEntry.getSignature().toString() |
| 144 | )); | 147 | )); |
| 145 | } | 148 | } |
| 146 | } | 149 | } |
| 147 | 150 | ||
| 148 | // translate all the class names referenced in the code | 151 | // translate all the class names referenced in the code |
| 149 | // the above code only changed method/field/reference names and types, but not the rest of the class references | 152 | // the above code only changed method/field/reference names and types, but not the rest of the class references |
| 150 | ClassRenamer.renameClasses(c, this.translator); | 153 | ClassRenamer.renameClasses(c, this.translator); |
| 151 | 154 | ||
| 152 | // translate the source file attribute too | 155 | // translate the source file attribute too |
| 153 | ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); | 156 | ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); |
| 154 | if (deobfClassEntry != null) { | 157 | if (deobfClassEntry != null) { |
| 155 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; | 158 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; |
| 156 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); | 159 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); |
| 157 | } | 160 | } |
| 158 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 161 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 159 | if (attr != null) | 162 | if (attr != null) |
| 160 | InnerClassWriter.changeModifier(c, attr, translator); | 163 | InnerClassWriter.changeModifier(c, attr, translator); |
| 161 | } | 164 | } |
| 162 | } | 165 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java index 256df61e..1932730d 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java | |||
| @@ -8,8 +8,15 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 14 | import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; | ||
| 15 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 16 | import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; | ||
| 17 | import javassist.bytecode.ConstPool; | ||
| 18 | import javassist.bytecode.Descriptor; | ||
| 19 | |||
| 13 | import java.io.DataInputStream; | 20 | import java.io.DataInputStream; |
| 14 | import java.io.DataOutputStream; | 21 | import java.io.DataOutputStream; |
| 15 | import java.lang.reflect.Constructor; | 22 | import java.lang.reflect.Constructor; |
| @@ -17,247 +24,241 @@ import java.lang.reflect.Field; | |||
| 17 | import java.lang.reflect.Method; | 24 | import java.lang.reflect.Method; |
| 18 | import java.util.HashMap; | 25 | import java.util.HashMap; |
| 19 | 26 | ||
| 20 | import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; | ||
| 21 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 22 | import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; | ||
| 23 | import javassist.bytecode.ConstPool; | ||
| 24 | import javassist.bytecode.Descriptor; | ||
| 25 | |||
| 26 | public class ConstPoolEditor { | 27 | public class ConstPoolEditor { |
| 27 | 28 | ||
| 28 | private static Method getItem; | 29 | private static Method getItem; |
| 29 | private static Method addItem; | 30 | private static Method addItem; |
| 30 | private static Method addItem0; | 31 | private static Method addItem0; |
| 31 | private static Field items; | 32 | private static Field items; |
| 32 | private static Field cache; | 33 | private static Field cache; |
| 33 | private static Field numItems; | 34 | private static Field numItems; |
| 34 | private static Field objects; | 35 | private static Field objects; |
| 35 | private static Field elements; | 36 | private static Field elements; |
| 36 | private static Method methodWritePool; | 37 | private static Method methodWritePool; |
| 37 | private static Constructor<ConstPool> constructorPool; | 38 | private static Constructor<ConstPool> constructorPool; |
| 38 | 39 | ||
| 39 | static { | 40 | static { |
| 40 | try { | 41 | try { |
| 41 | getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); | 42 | getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); |
| 42 | getItem.setAccessible(true); | 43 | getItem.setAccessible(true); |
| 43 | 44 | ||
| 44 | addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); | 45 | addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); |
| 45 | addItem.setAccessible(true); | 46 | addItem.setAccessible(true); |
| 46 | 47 | ||
| 47 | addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); | 48 | addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); |
| 48 | addItem0.setAccessible(true); | 49 | addItem0.setAccessible(true); |
| 49 | 50 | ||
| 50 | items = ConstPool.class.getDeclaredField("items"); | 51 | items = ConstPool.class.getDeclaredField("items"); |
| 51 | items.setAccessible(true); | 52 | items.setAccessible(true); |
| 52 | 53 | ||
| 53 | cache = ConstPool.class.getDeclaredField("itemsCache"); | 54 | cache = ConstPool.class.getDeclaredField("itemsCache"); |
| 54 | cache.setAccessible(true); | 55 | cache.setAccessible(true); |
| 55 | 56 | ||
| 56 | numItems = ConstPool.class.getDeclaredField("numOfItems"); | 57 | numItems = ConstPool.class.getDeclaredField("numOfItems"); |
| 57 | numItems.setAccessible(true); | 58 | numItems.setAccessible(true); |
| 58 | 59 | ||
| 59 | objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); | 60 | objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); |
| 60 | objects.setAccessible(true); | 61 | objects.setAccessible(true); |
| 61 | 62 | ||
| 62 | elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); | 63 | elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); |
| 63 | elements.setAccessible(true); | 64 | elements.setAccessible(true); |
| 64 | 65 | ||
| 65 | methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); | 66 | methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); |
| 66 | methodWritePool.setAccessible(true); | 67 | methodWritePool.setAccessible(true); |
| 67 | 68 | ||
| 68 | constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); | 69 | constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); |
| 69 | constructorPool.setAccessible(true); | 70 | constructorPool.setAccessible(true); |
| 70 | } catch (Exception ex) { | 71 | } catch (Exception ex) { |
| 71 | throw new Error(ex); | 72 | throw new Error(ex); |
| 72 | } | 73 | } |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | private ConstPool pool; | 76 | private ConstPool pool; |
| 76 | 77 | ||
| 77 | public ConstPoolEditor(ConstPool pool) { | 78 | public ConstPoolEditor(ConstPool pool) { |
| 78 | this.pool = pool; | 79 | this.pool = pool; |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | public void writePool(DataOutputStream out) { | 82 | public static ConstPool readPool(DataInputStream in) { |
| 82 | try { | 83 | try { |
| 83 | methodWritePool.invoke(this.pool, out); | 84 | return constructorPool.newInstance(in); |
| 84 | } catch (Exception ex) { | 85 | } catch (Exception ex) { |
| 85 | throw new Error(ex); | 86 | throw new Error(ex); |
| 86 | } | 87 | } |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 89 | public static ConstPool readPool(DataInputStream in) { | 90 | public static ConstPool newConstPool() { |
| 90 | try { | 91 | // const pool expects the name of a class to initialize itself |
| 91 | return constructorPool.newInstance(in); | 92 | // but we want an empty pool |
| 92 | } catch (Exception ex) { | 93 | // so give it a bogus name, and then clear the entries afterwards |
| 93 | throw new Error(ex); | 94 | ConstPool pool = new ConstPool("a"); |
| 94 | } | 95 | |
| 95 | } | 96 | ConstPoolEditor editor = new ConstPoolEditor(pool); |
| 96 | 97 | int size = pool.getSize(); | |
| 97 | public String getMemberrefClassname(int memberrefIndex) { | 98 | for (int i = 0; i < size - 1; i++) { |
| 98 | return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); | 99 | editor.removeLastItem(); |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | public String getMemberrefName(int memberrefIndex) { | 102 | // make sure the pool is actually empty |
| 102 | return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); | 103 | // although, in this case "empty" means one thing in it |
| 103 | } | 104 | // the JVM spec says index 0 should be reserved |
| 104 | 105 | assert (pool.getSize() == 1); | |
| 105 | public String getMemberrefType(int memberrefIndex) { | 106 | assert (editor.getItem(0) == null); |
| 106 | return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); | 107 | assert (editor.getItem(1) == null); |
| 107 | } | 108 | assert (editor.getItem(2) == null); |
| 108 | 109 | assert (editor.getItem(3) == null); | |
| 109 | public ConstInfoAccessor getItem(int index) { | 110 | |
| 110 | try { | 111 | // also, clear the cache |
| 111 | Object entry = getItem.invoke(this.pool, index); | 112 | editor.getCache().clear(); |
| 112 | if (entry == null) { | 113 | |
| 113 | return null; | 114 | return pool; |
| 114 | } | 115 | } |
| 115 | return new ConstInfoAccessor(entry); | 116 | |
| 116 | } catch (Exception ex) { | 117 | public void writePool(DataOutputStream out) { |
| 117 | throw new Error(ex); | 118 | try { |
| 118 | } | 119 | methodWritePool.invoke(this.pool, out); |
| 119 | } | 120 | } catch (Exception ex) { |
| 120 | 121 | throw new Error(ex); | |
| 121 | public int addItem(Object item) { | 122 | } |
| 122 | try { | 123 | } |
| 123 | return (Integer) addItem.invoke(this.pool, item); | 124 | |
| 124 | } catch (Exception ex) { | 125 | public String getMemberrefClassname(int memberrefIndex) { |
| 125 | throw new Error(ex); | 126 | return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); |
| 126 | } | 127 | } |
| 127 | } | 128 | |
| 128 | 129 | public String getMemberrefName(int memberrefIndex) { | |
| 129 | public int addItemForceNew(Object item) { | 130 | return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); |
| 130 | try { | 131 | } |
| 131 | return (Integer) addItem0.invoke(this.pool, item); | 132 | |
| 132 | } catch (Exception ex) { | 133 | public String getMemberrefType(int memberrefIndex) { |
| 133 | throw new Error(ex); | 134 | return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); |
| 134 | } | 135 | } |
| 135 | } | 136 | |
| 136 | 137 | public ConstInfoAccessor getItem(int index) { | |
| 137 | @SuppressWarnings("rawtypes") | 138 | try { |
| 138 | public void removeLastItem() { | 139 | Object entry = getItem.invoke(this.pool, index); |
| 139 | try { | 140 | if (entry == null) { |
| 140 | // remove the item from the cache | 141 | return null; |
| 141 | HashMap cache = getCache(); | 142 | } |
| 142 | if (cache != null) { | 143 | return new ConstInfoAccessor(entry); |
| 143 | Object item = getItem(this.pool.getSize() - 1); | 144 | } catch (Exception ex) { |
| 144 | cache.remove(item); | 145 | throw new Error(ex); |
| 145 | } | 146 | } |
| 146 | 147 | } | |
| 147 | // remove the actual item | 148 | |
| 148 | // based off of LongVector.addElement() | 149 | public int addItem(Object item) { |
| 149 | Object item = items.get(this.pool); | 150 | try { |
| 150 | Object[][] object = (Object[][]) objects.get(items); | 151 | return (Integer) addItem.invoke(this.pool, item); |
| 151 | int numElements = (Integer) elements.get(items) - 1; | 152 | } catch (Exception ex) { |
| 152 | int nth = numElements >> 7; | 153 | throw new Error(ex); |
| 153 | int offset = numElements & (128 - 1); | 154 | } |
| 154 | object[nth][offset] = null; | 155 | } |
| 155 | 156 | ||
| 156 | // decrement the number of items | 157 | public int addItemForceNew(Object item) { |
| 157 | elements.set(item, numElements); | 158 | try { |
| 158 | numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); | 159 | return (Integer) addItem0.invoke(this.pool, item); |
| 159 | } catch (Exception ex) { | 160 | } catch (Exception ex) { |
| 160 | throw new Error(ex); | 161 | throw new Error(ex); |
| 161 | } | 162 | } |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 164 | @SuppressWarnings("rawtypes") | 165 | @SuppressWarnings("rawtypes") |
| 165 | public HashMap getCache() { | 166 | public void removeLastItem() { |
| 166 | try { | 167 | try { |
| 167 | return (HashMap) cache.get(this.pool); | 168 | // remove the item from the cache |
| 168 | } catch (Exception ex) { | 169 | HashMap cache = getCache(); |
| 169 | throw new Error(ex); | 170 | if (cache != null) { |
| 170 | } | 171 | Object item = getItem(this.pool.getSize() - 1); |
| 171 | } | 172 | cache.remove(item); |
| 172 | 173 | } | |
| 173 | @SuppressWarnings({"rawtypes", "unchecked"}) | 174 | |
| 174 | public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { | 175 | // remove the actual item |
| 175 | // NOTE: when changing values, we always need to copy-on-write | 176 | // based off of LongVector.addElement() |
| 176 | try { | 177 | Object item = items.get(this.pool); |
| 177 | // get the memberref item | 178 | Object[][] object = (Object[][]) objects.get(items); |
| 178 | Object item = getItem(memberrefIndex).getItem(); | 179 | int numElements = (Integer) elements.get(items) - 1; |
| 179 | 180 | int nth = numElements >> 7; | |
| 180 | // update the cache | 181 | int offset = numElements & (128 - 1); |
| 181 | HashMap cache = getCache(); | 182 | object[nth][offset] = null; |
| 182 | if (cache != null) { | 183 | |
| 183 | cache.remove(item); | 184 | // decrement the number of items |
| 184 | } | 185 | elements.set(item, numElements); |
| 185 | 186 | numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); | |
| 186 | new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); | 187 | } catch (Exception ex) { |
| 187 | 188 | throw new Error(ex); | |
| 188 | // update the cache | 189 | } |
| 189 | if (cache != null) { | 190 | } |
| 190 | cache.put(item, item); | 191 | |
| 191 | } | 192 | @SuppressWarnings("rawtypes") |
| 192 | } catch (Exception ex) { | 193 | public HashMap getCache() { |
| 193 | throw new Error(ex); | 194 | try { |
| 194 | } | 195 | return (HashMap) cache.get(this.pool); |
| 195 | 196 | } catch (Exception ex) { | |
| 196 | // make sure the change worked | 197 | throw new Error(ex); |
| 197 | assert (newName.equals(getMemberrefName(memberrefIndex))); | 198 | } |
| 198 | assert (newType.equals(getMemberrefType(memberrefIndex))); | 199 | } |
| 199 | } | 200 | |
| 200 | 201 | @SuppressWarnings({ "rawtypes", "unchecked" }) | |
| 201 | @SuppressWarnings({"rawtypes", "unchecked"}) | 202 | public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { |
| 202 | public void changeClassName(int classNameIndex, String newName) { | 203 | // NOTE: when changing values, we always need to copy-on-write |
| 203 | // NOTE: when changing values, we always need to copy-on-write | 204 | try { |
| 204 | try { | 205 | // get the memberref item |
| 205 | // get the class item | 206 | Object item = getItem(memberrefIndex).getItem(); |
| 206 | Object item = getItem(classNameIndex).getItem(); | 207 | |
| 207 | 208 | // update the cache | |
| 208 | // update the cache | 209 | HashMap cache = getCache(); |
| 209 | HashMap cache = getCache(); | 210 | if (cache != null) { |
| 210 | if (cache != null) { | 211 | cache.remove(item); |
| 211 | cache.remove(item); | 212 | } |
| 212 | } | 213 | |
| 213 | 214 | new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); | |
| 214 | // add the new name and repoint the name-and-type to it | 215 | |
| 215 | new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); | 216 | // update the cache |
| 216 | 217 | if (cache != null) { | |
| 217 | // update the cache | 218 | cache.put(item, item); |
| 218 | if (cache != null) { | 219 | } |
| 219 | cache.put(item, item); | 220 | } catch (Exception ex) { |
| 220 | } | 221 | throw new Error(ex); |
| 221 | } catch (Exception ex) { | 222 | } |
| 222 | throw new Error(ex); | 223 | |
| 223 | } | 224 | // make sure the change worked |
| 224 | } | 225 | assert (newName.equals(getMemberrefName(memberrefIndex))); |
| 225 | 226 | assert (newType.equals(getMemberrefType(memberrefIndex))); | |
| 226 | public static ConstPool newConstPool() { | 227 | } |
| 227 | // const pool expects the name of a class to initialize itself | 228 | |
| 228 | // but we want an empty pool | 229 | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| 229 | // so give it a bogus name, and then clear the entries afterwards | 230 | public void changeClassName(int classNameIndex, String newName) { |
| 230 | ConstPool pool = new ConstPool("a"); | 231 | // NOTE: when changing values, we always need to copy-on-write |
| 231 | 232 | try { | |
| 232 | ConstPoolEditor editor = new ConstPoolEditor(pool); | 233 | // get the class item |
| 233 | int size = pool.getSize(); | 234 | Object item = getItem(classNameIndex).getItem(); |
| 234 | for (int i = 0; i < size - 1; i++) { | 235 | |
| 235 | editor.removeLastItem(); | 236 | // update the cache |
| 236 | } | 237 | HashMap cache = getCache(); |
| 237 | 238 | if (cache != null) { | |
| 238 | // make sure the pool is actually empty | 239 | cache.remove(item); |
| 239 | // although, in this case "empty" means one thing in it | 240 | } |
| 240 | // the JVM spec says index 0 should be reserved | 241 | |
| 241 | assert (pool.getSize() == 1); | 242 | // add the new name and repoint the name-and-type to it |
| 242 | assert (editor.getItem(0) == null); | 243 | new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); |
| 243 | assert (editor.getItem(1) == null); | 244 | |
| 244 | assert (editor.getItem(2) == null); | 245 | // update the cache |
| 245 | assert (editor.getItem(3) == null); | 246 | if (cache != null) { |
| 246 | 247 | cache.put(item, item); | |
| 247 | // also, clear the cache | 248 | } |
| 248 | editor.getCache().clear(); | 249 | } catch (Exception ex) { |
| 249 | 250 | throw new Error(ex); | |
| 250 | return pool; | 251 | } |
| 251 | } | 252 | } |
| 252 | 253 | ||
| 253 | public String dump() { | 254 | public String dump() { |
| 254 | StringBuilder buf = new StringBuilder(); | 255 | StringBuilder buf = new StringBuilder(); |
| 255 | for (int i = 1; i < this.pool.getSize(); i++) { | 256 | for (int i = 1; i < this.pool.getSize(); i++) { |
| 256 | buf.append(String.format("%4d", i)); | 257 | buf.append(String.format("%4d", i)); |
| 257 | buf.append(" "); | 258 | buf.append(" "); |
| 258 | buf.append(getItem(i).toString()); | 259 | buf.append(getItem(i)); |
| 259 | buf.append("\n"); | 260 | buf.append("\n"); |
| 260 | } | 261 | } |
| 261 | return buf.toString(); | 262 | return buf.toString(); |
| 262 | } | 263 | } |
| 263 | } | 264 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java index 21b04173..9013d581 100644 --- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java +++ b/src/main/java/cuchaz/enigma/bytecode/InfoType.java | |||
| @@ -8,259 +8,259 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 15 | import cuchaz.enigma.bytecode.accessors.*; | ||
| 14 | 16 | ||
| 15 | import java.util.Collection; | 17 | import java.util.Collection; |
| 16 | import java.util.Map; | 18 | import java.util.Map; |
| 17 | 19 | ||
| 18 | import cuchaz.enigma.bytecode.accessors.*; | ||
| 19 | |||
| 20 | public enum InfoType { | 20 | public enum InfoType { |
| 21 | 21 | ||
| 22 | Utf8Info(1), | 22 | Utf8Info(1), |
| 23 | IntegerInfo(3), | 23 | IntegerInfo(3), |
| 24 | FloatInfo(4), | 24 | FloatInfo(4), |
| 25 | LongInfo(5), | 25 | LongInfo(5), |
| 26 | DoubleInfo(6), | 26 | DoubleInfo(6), |
| 27 | ClassInfo(7) { | 27 | ClassInfo(7) { |
| 28 | @Override | 28 | @Override |
| 29 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 29 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 30 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | 30 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); |
| 31 | gatherIndexTree(indices, editor, accessor.getNameIndex()); | 31 | gatherIndexTree(indices, editor, accessor.getNameIndex()); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | @Override | 34 | @Override |
| 35 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 35 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 36 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | 36 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); |
| 37 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); | 37 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | @Override | 40 | @Override |
| 41 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 41 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 42 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | 42 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); |
| 43 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); | 43 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); |
| 44 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); | 44 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); |
| 45 | } | 45 | } |
| 46 | }, | 46 | }, |
| 47 | StringInfo(8) { | 47 | StringInfo(8) { |
| 48 | @Override | 48 | @Override |
| 49 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 49 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 50 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | 50 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); |
| 51 | gatherIndexTree(indices, editor, accessor.getStringIndex()); | 51 | gatherIndexTree(indices, editor, accessor.getStringIndex()); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | @Override | 54 | @Override |
| 55 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 55 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 56 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | 56 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); |
| 57 | accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); | 57 | accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | @Override | 60 | @Override |
| 61 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 61 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 62 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | 62 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); |
| 63 | ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); | 63 | ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); |
| 64 | return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); | 64 | return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); |
| 65 | } | 65 | } |
| 66 | }, | 66 | }, |
| 67 | FieldRefInfo(9) { | 67 | FieldRefInfo(9) { |
| 68 | @Override | 68 | @Override |
| 69 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 69 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 70 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | 70 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); |
| 71 | gatherIndexTree(indices, editor, accessor.getClassIndex()); | 71 | gatherIndexTree(indices, editor, accessor.getClassIndex()); |
| 72 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); | 72 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | @Override | 75 | @Override |
| 76 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 76 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 77 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | 77 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); |
| 78 | accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); | 78 | accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); |
| 79 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); | 79 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | @Override | 82 | @Override |
| 83 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 83 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 84 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | 84 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); |
| 85 | ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); | 85 | ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); |
| 86 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); | 86 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); |
| 87 | return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); | 87 | return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); |
| 88 | } | 88 | } |
| 89 | }, | 89 | }, |
| 90 | // same as FieldRefInfo | 90 | // same as FieldRefInfo |
| 91 | MethodRefInfo(10) { | 91 | MethodRefInfo(10) { |
| 92 | @Override | 92 | @Override |
| 93 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 93 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 94 | FieldRefInfo.gatherIndexTree(indices, editor, entry); | 94 | FieldRefInfo.gatherIndexTree(indices, editor, entry); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | @Override | 97 | @Override |
| 98 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 98 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 99 | FieldRefInfo.remapIndices(map, entry); | 99 | FieldRefInfo.remapIndices(map, entry); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | @Override | 102 | @Override |
| 103 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 103 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 104 | return FieldRefInfo.subIndicesAreValid(entry, pool); | 104 | return FieldRefInfo.subIndicesAreValid(entry, pool); |
| 105 | } | 105 | } |
| 106 | }, | 106 | }, |
| 107 | // same as FieldRefInfo | 107 | // same as FieldRefInfo |
| 108 | InterfaceMethodRefInfo(11) { | 108 | InterfaceMethodRefInfo(11) { |
| 109 | @Override | 109 | @Override |
| 110 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 110 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 111 | FieldRefInfo.gatherIndexTree(indices, editor, entry); | 111 | FieldRefInfo.gatherIndexTree(indices, editor, entry); |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | @Override | 114 | @Override |
| 115 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 115 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 116 | FieldRefInfo.remapIndices(map, entry); | 116 | FieldRefInfo.remapIndices(map, entry); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | @Override | 119 | @Override |
| 120 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 120 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 121 | return FieldRefInfo.subIndicesAreValid(entry, pool); | 121 | return FieldRefInfo.subIndicesAreValid(entry, pool); |
| 122 | } | 122 | } |
| 123 | }, | 123 | }, |
| 124 | NameAndTypeInfo(12) { | 124 | NameAndTypeInfo(12) { |
| 125 | @Override | 125 | @Override |
| 126 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 126 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 127 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | 127 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); |
| 128 | gatherIndexTree(indices, editor, accessor.getNameIndex()); | 128 | gatherIndexTree(indices, editor, accessor.getNameIndex()); |
| 129 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | 129 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | @Override | 132 | @Override |
| 133 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 133 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 134 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | 134 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); |
| 135 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); | 135 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); |
| 136 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | 136 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | @Override | 139 | @Override |
| 140 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 140 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 141 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | 141 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); |
| 142 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); | 142 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); |
| 143 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | 143 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); |
| 144 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); | 144 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); |
| 145 | } | 145 | } |
| 146 | }, | 146 | }, |
| 147 | MethodHandleInfo(15) { | 147 | MethodHandleInfo(15) { |
| 148 | @Override | 148 | @Override |
| 149 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 149 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 150 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | 150 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); |
| 151 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | 151 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); |
| 152 | gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); | 152 | gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | @Override | 155 | @Override |
| 156 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 156 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 157 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | 157 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); |
| 158 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | 158 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); |
| 159 | accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); | 159 | accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | @Override | 162 | @Override |
| 163 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 163 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 164 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | 164 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); |
| 165 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | 165 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); |
| 166 | ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); | 166 | ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); |
| 167 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); | 167 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); |
| 168 | } | 168 | } |
| 169 | }, | 169 | }, |
| 170 | MethodTypeInfo(16) { | 170 | MethodTypeInfo(16) { |
| 171 | @Override | 171 | @Override |
| 172 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 172 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 173 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | 173 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); |
| 174 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | 174 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | @Override | 177 | @Override |
| 178 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 178 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 179 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | 179 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); |
| 180 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | 180 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | @Override | 183 | @Override |
| 184 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 184 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 185 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | 185 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); |
| 186 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | 186 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); |
| 187 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); | 187 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); |
| 188 | } | 188 | } |
| 189 | }, | 189 | }, |
| 190 | InvokeDynamicInfo(18) { | 190 | InvokeDynamicInfo(18) { |
| 191 | @Override | 191 | @Override |
| 192 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 192 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 193 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | 193 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); |
| 194 | gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); | 194 | gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); |
| 195 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); | 195 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | @Override | 198 | @Override |
| 199 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 199 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { |
| 200 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | 200 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); |
| 201 | accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); | 201 | accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); |
| 202 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); | 202 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | @Override | 205 | @Override |
| 206 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 206 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 207 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | 207 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); |
| 208 | ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); | 208 | ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); |
| 209 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); | 209 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); |
| 210 | return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); | 210 | return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); |
| 211 | } | 211 | } |
| 212 | }; | 212 | }; |
| 213 | 213 | ||
| 214 | private static Map<Integer, InfoType> types; | 214 | private static Map<Integer, InfoType> types; |
| 215 | 215 | ||
| 216 | static { | 216 | static { |
| 217 | types = Maps.newTreeMap(); | 217 | types = Maps.newTreeMap(); |
| 218 | for (InfoType type : values()) { | 218 | for (InfoType type : values()) { |
| 219 | types.put(type.getTag(), type); | 219 | types.put(type.getTag(), type); |
| 220 | } | 220 | } |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | private int tag; | 223 | private int tag; |
| 224 | 224 | ||
| 225 | InfoType(int tag) { | 225 | InfoType(int tag) { |
| 226 | this.tag = tag; | 226 | this.tag = tag; |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | public int getTag() { | 229 | public static InfoType getByTag(int tag) { |
| 230 | return this.tag; | 230 | return types.get(tag); |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | 233 | public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { |
| 234 | // by default, do nothing | 234 | // add own index |
| 235 | } | 235 | indices.add(index); |
| 236 | 236 | ||
| 237 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | 237 | // recurse |
| 238 | // by default, do nothing | 238 | ConstInfoAccessor entry = editor.getItem(index); |
| 239 | } | 239 | entry.getType().gatherIndexTree(indices, editor, entry); |
| 240 | 240 | } | |
| 241 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | 241 | |
| 242 | // by default, everything is good | 242 | private static int remapIndex(Map<Integer, Integer> map, int index) { |
| 243 | return true; | 243 | Integer newIndex = map.get(index); |
| 244 | } | 244 | if (newIndex == null) { |
| 245 | 245 | newIndex = index; | |
| 246 | public static InfoType getByTag(int tag) { | 246 | } |
| 247 | return types.get(tag); | 247 | return newIndex; |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { | 250 | public int getTag() { |
| 251 | // add own index | 251 | return this.tag; |
| 252 | indices.add(index); | 252 | } |
| 253 | 253 | ||
| 254 | // recurse | 254 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { |
| 255 | ConstInfoAccessor entry = editor.getItem(index); | 255 | // by default, do nothing |
| 256 | entry.getType().gatherIndexTree(indices, editor, entry); | 256 | } |
| 257 | } | 257 | |
| 258 | 258 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | |
| 259 | private static int remapIndex(Map<Integer, Integer> map, int index) { | 259 | // by default, do nothing |
| 260 | Integer newIndex = map.get(index); | 260 | } |
| 261 | if (newIndex == null) { | 261 | |
| 262 | newIndex = index; | 262 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { |
| 263 | } | 263 | // by default, everything is good |
| 264 | return newIndex; | 264 | return true; |
| 265 | } | 265 | } |
| 266 | } | 266 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java index eb70c23d..5f8be908 100644 --- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java | |||
| @@ -8,13 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | |||
| 15 | import java.util.Collection; | ||
| 16 | import java.util.List; | ||
| 17 | |||
| 18 | import cuchaz.enigma.analysis.JarIndex; | 15 | import cuchaz.enigma.analysis.JarIndex; |
| 19 | import cuchaz.enigma.mapping.*; | 16 | import cuchaz.enigma.mapping.*; |
| 20 | import javassist.ClassPool; | 17 | import javassist.ClassPool; |
| @@ -22,126 +19,126 @@ import javassist.CtClass; | |||
| 22 | import javassist.NotFoundException; | 19 | import javassist.NotFoundException; |
| 23 | import javassist.bytecode.*; | 20 | import javassist.bytecode.*; |
| 24 | 21 | ||
| 22 | import java.util.Collection; | ||
| 23 | import java.util.List; | ||
| 24 | |||
| 25 | public class InnerClassWriter { | 25 | public class InnerClassWriter { |
| 26 | 26 | ||
| 27 | private JarIndex index; | 27 | private JarIndex index; |
| 28 | private Translator deobfuscatorTranslator; | 28 | private Translator deobfuscatorTranslator; |
| 29 | 29 | ||
| 30 | public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { | 30 | public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { |
| 31 | this.index = index; | 31 | this.index = index; |
| 32 | this.deobfuscatorTranslator = deobfuscatorTranslator; | 32 | this.deobfuscatorTranslator = deobfuscatorTranslator; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | public void write(CtClass c) { | 35 | // FIXME: modiffier is not applied to inner class |
| 36 | 36 | public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) { | |
| 37 | // don't change anything if there's already an attribute there | 37 | ClassPool pool = c.getClassPool(); |
| 38 | InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 38 | for (int i = 0; i < attr.tableLength(); i++) { |
| 39 | if (oldAttr != null) { | 39 | |
| 40 | // bail! | 40 | String innerName = attr.innerClass(i); |
| 41 | return; | 41 | // get the inner class full name (which has already been translated) |
| 42 | } | 42 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); |
| 43 | 43 | try { | |
| 44 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); | 44 | CtClass innerClass = pool.get(innerName); |
| 45 | List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry); | 45 | Mappings.EntryModifier modifier = translator.getModifier(classEntry); |
| 46 | 46 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | |
| 47 | boolean isInnerClass = obfClassChain.size() > 1; | 47 | ClassRenamer.applyModifier(innerClass, modifier); |
| 48 | if (isInnerClass) { | 48 | } catch (NotFoundException e) { |
| 49 | 49 | // This shouldn't be possible in theory | |
| 50 | // it's an inner class, rename it to the fully qualified name | 50 | //e.printStackTrace(); |
| 51 | c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); | 51 | } |
| 52 | 52 | } | |
| 53 | BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); | 53 | } |
| 54 | if (caller != null) { | 54 | |
| 55 | 55 | public void write(CtClass c) { | |
| 56 | // write the enclosing method attribute | 56 | |
| 57 | if (caller.getName().equals("<clinit>")) { | 57 | // don't change anything if there's already an attribute there |
| 58 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); | 58 | InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 59 | } else { | 59 | if (oldAttr != null) { |
| 60 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); | 60 | // bail! |
| 61 | } | 61 | return; |
| 62 | } | 62 | } |
| 63 | } | 63 | |
| 64 | 64 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); | |
| 65 | // does this class have any inner classes? | 65 | List<ClassEntry> obfClassChain = this.index.getObfClassChain(obfClassEntry); |
| 66 | Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); | 66 | |
| 67 | 67 | boolean isInnerClass = obfClassChain.size() > 1; | |
| 68 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { | 68 | if (isInnerClass) { |
| 69 | 69 | ||
| 70 | // create an inner class attribute | 70 | // it's an inner class, rename it to the fully qualified name |
| 71 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); | 71 | c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); |
| 72 | c.getClassFile().addAttribute(attr); | 72 | |
| 73 | 73 | BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); | |
| 74 | // write the ancestry, but not the outermost class | 74 | if (caller != null) { |
| 75 | for (int i = 1; i < obfClassChain.size(); i++) { | 75 | |
| 76 | ClassEntry obfInnerClassEntry = obfClassChain.get(i); | 76 | // write the enclosing method attribute |
| 77 | writeInnerClass(attr, obfClassChain, obfInnerClassEntry); | 77 | if (caller.getName().equals("<clinit>")) { |
| 78 | 78 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); | |
| 79 | // update references to use the fully qualified inner class name | 79 | } else { |
| 80 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); | 80 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); |
| 81 | } | 81 | } |
| 82 | 82 | } | |
| 83 | // write the inner classes | 83 | } |
| 84 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { | 84 | |
| 85 | 85 | // does this class have any inner classes? | |
| 86 | // extend the class chain | 86 | Collection<ClassEntry> obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); |
| 87 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); | 87 | |
| 88 | extendedObfClassChain.add(obfInnerClassEntry); | 88 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { |
| 89 | 89 | ||
| 90 | writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); | 90 | // create an inner class attribute |
| 91 | 91 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); | |
| 92 | // update references to use the fully qualified inner class name | 92 | c.getClassFile().addAttribute(attr); |
| 93 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); | 93 | |
| 94 | } | 94 | // write the ancestry, but not the outermost class |
| 95 | } | 95 | for (int i = 1; i < obfClassChain.size(); i++) { |
| 96 | } | 96 | ClassEntry obfInnerClassEntry = obfClassChain.get(i); |
| 97 | 97 | writeInnerClass(attr, obfClassChain, obfInnerClassEntry); | |
| 98 | // FIXME: modiffier is not applied to inner class | 98 | |
| 99 | public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) | 99 | // update references to use the fully qualified inner class name |
| 100 | { | 100 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); |
| 101 | ClassPool pool = c.getClassPool(); | 101 | } |
| 102 | for (int i = 0; i < attr.tableLength(); i++) { | 102 | |
| 103 | 103 | // write the inner classes | |
| 104 | String innerName = attr.innerClass(i); | 104 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { |
| 105 | // get the inner class full name (which has already been translated) | 105 | |
| 106 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); | 106 | // extend the class chain |
| 107 | try | 107 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); |
| 108 | { | 108 | extendedObfClassChain.add(obfInnerClassEntry); |
| 109 | CtClass innerClass = pool.get(innerName); | 109 | |
| 110 | Mappings.EntryModifier modifier = translator.getModifier(classEntry); | 110 | writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); |
| 111 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | 111 | |
| 112 | ClassRenamer.applyModifier(innerClass, modifier); | 112 | // update references to use the fully qualified inner class name |
| 113 | } catch (NotFoundException e) | 113 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); |
| 114 | { | 114 | } |
| 115 | // This shouldn't be possible in theory | 115 | } |
| 116 | //e.printStackTrace(); | 116 | } |
| 117 | } | 117 | |
| 118 | } | 118 | private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { |
| 119 | } | 119 | |
| 120 | 120 | // get the new inner class name | |
| 121 | private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { | 121 | ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); |
| 122 | 122 | ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); | |
| 123 | // get the new inner class name | 123 | |
| 124 | ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); | 124 | // here's what the JVM spec says about the InnerClasses attribute |
| 125 | ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); | 125 | // append(inner, parent, 0 if anonymous else simple name, flags); |
| 126 | 126 | ||
| 127 | // here's what the JVM spec says about the InnerClasses attribute | 127 | // update the attribute with this inner class |
| 128 | // append(inner, parent, 0 if anonymous else simple name, flags); | 128 | ConstPool constPool = attr.getConstPool(); |
| 129 | 129 | int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); | |
| 130 | // update the attribute with this inner class | 130 | int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); |
| 131 | ConstPool constPool = attr.getConstPool(); | 131 | int innerClassNameIndex = 0; |
| 132 | int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); | 132 | int accessFlags = AccessFlag.PUBLIC; |
| 133 | int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); | 133 | // TODO: need to figure out if we can put static or not |
| 134 | int innerClassNameIndex = 0; | 134 | if (!this.index.isAnonymousClass(obfClassEntry)) { |
| 135 | int accessFlags = AccessFlag.PUBLIC; | 135 | innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); |
| 136 | // TODO: need to figure out if we can put static or not | 136 | } |
| 137 | if (!this.index.isAnonymousClass(obfClassEntry)) { | 137 | |
| 138 | innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); | 138 | attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); |
| 139 | } | ||
| 140 | |||
| 141 | attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); | ||
| 142 | 139 | ||
| 143 | /* DEBUG | 140 | /* DEBUG |
| 144 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", | 141 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", |
| 145 | obfClassEntry, | 142 | obfClassEntry, |
| 146 | attr.innerClass(attr.tableLength() - 1), | 143 | attr.innerClass(attr.tableLength() - 1), |
| 147 | attr.outerClass(attr.tableLength() - 1), | 144 | attr.outerClass(attr.tableLength() - 1), |
| @@ -150,5 +147,5 @@ public class InnerClassWriter { | |||
| 150 | obfClassEntry.getName() | 147 | obfClassEntry.getName() |
| 151 | )); | 148 | )); |
| 152 | */ | 149 | */ |
| 153 | } | 150 | } |
| 154 | } | 151 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java index 24b5f363..8909d816 100644 --- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java +++ b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.*; | 14 | import cuchaz.enigma.mapping.*; |
| @@ -15,131 +16,129 @@ import javassist.CtBehavior; | |||
| 15 | import javassist.CtClass; | 16 | import javassist.CtClass; |
| 16 | import javassist.bytecode.*; | 17 | import javassist.bytecode.*; |
| 17 | 18 | ||
| 18 | |||
| 19 | public class LocalVariableRenamer { | 19 | public class LocalVariableRenamer { |
| 20 | 20 | ||
| 21 | private Translator translator; | 21 | private Translator translator; |
| 22 | 22 | ||
| 23 | public LocalVariableRenamer(Translator translator) { | 23 | public LocalVariableRenamer(Translator translator) { |
| 24 | this.translator = translator; | 24 | this.translator = translator; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | public void rename(CtClass c) { | 27 | public void rename(CtClass c) { |
| 28 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 28 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 29 | 29 | ||
| 30 | // if there's a local variable table, just rename everything to v1, v2, v3, ... for now | 30 | // if there's a local variable table, just rename everything to v1, v2, v3, ... for now |
| 31 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); | 31 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); |
| 32 | if (codeAttribute == null) { | 32 | if (codeAttribute == null) { |
| 33 | continue; | 33 | continue; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 36 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 37 | ConstPool constants = c.getClassFile().getConstPool(); | 37 | ConstPool constants = c.getClassFile().getConstPool(); |
| 38 | 38 | ||
| 39 | LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); | 39 | LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); |
| 40 | if (table != null) { | 40 | if (table != null) { |
| 41 | renameLVT(behaviorEntry, constants, table); | 41 | renameLVT(behaviorEntry, constants, table); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); | 44 | LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); |
| 45 | if (typeTable != null) { | 45 | if (typeTable != null) { |
| 46 | renameLVTT(typeTable, table); | 46 | renameLVTT(typeTable, table); |
| 47 | } | 47 | } |
| 48 | } | 48 | } |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | // DEBUG | 51 | // DEBUG |
| 52 | @SuppressWarnings("unused") | 52 | @SuppressWarnings("unused") |
| 53 | private void dumpTable(LocalVariableAttribute table) { | 53 | private void dumpTable(LocalVariableAttribute table) { |
| 54 | for (int i = 0; i < table.tableLength(); i++) { | 54 | for (int i = 0; i < table.tableLength(); i++) { |
| 55 | System.out.println(String.format("\t%d (%d): %s %s", | 55 | System.out.println(String.format("\t%d (%d): %s %s", |
| 56 | i, table.index(i), table.variableName(i), table.descriptor(i) | 56 | i, table.index(i), table.variableName(i), table.descriptor(i) |
| 57 | )); | 57 | )); |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { | 61 | private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { |
| 62 | 62 | ||
| 63 | // skip empty tables | 63 | // skip empty tables |
| 64 | if (table.tableLength() <= 0) { | 64 | if (table.tableLength() <= 0) { |
| 65 | return; | 65 | return; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | // where do we start counting variables? | 68 | // where do we start counting variables? |
| 69 | int starti = 0; | 69 | int starti = 0; |
| 70 | if (table.variableName(0).equals("this")) { | 70 | if (table.variableName(0).equals("this")) { |
| 71 | // skip the "this" variable | 71 | // skip the "this" variable |
| 72 | starti = 1; | 72 | starti = 1; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | // rename method arguments first | 75 | // rename method arguments first |
| 76 | int numArgs = 0; | 76 | int numArgs = 0; |
| 77 | if (behaviorEntry.getSignature() != null) { | 77 | if (behaviorEntry.getSignature() != null) { |
| 78 | numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); | 78 | numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); |
| 79 | 79 | ||
| 80 | boolean isNestedClassConstructor = false; | 80 | boolean isNestedClassConstructor = false; |
| 81 | 81 | ||
| 82 | // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! | 82 | // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! |
| 83 | if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) | 83 | if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { |
| 84 | { | 84 | // Get the first arg type |
| 85 | // Get the first arg type | 85 | Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); |
| 86 | Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); | 86 | |
| 87 | 87 | // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class | |
| 88 | // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class | 88 | if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { |
| 89 | if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { | 89 | isNestedClassConstructor = true; |
| 90 | isNestedClassConstructor = true; | 90 | numArgs--; |
| 91 | numArgs--; | 91 | } |
| 92 | } | 92 | } |
| 93 | } | 93 | |
| 94 | 94 | for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { | |
| 95 | for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { | 95 | int argi = i - starti; |
| 96 | int argi = i - starti; | 96 | String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); |
| 97 | String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); | 97 | if (argName == null) { |
| 98 | if (argName == null) { | 98 | Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi); |
| 99 | Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi); | 99 | // Unfortunately each of these have different name getters, so they have different code paths |
| 100 | // Unfortunately each of these have different name getters, so they have different code paths | 100 | if (argType.isPrimitive()) { |
| 101 | if (argType.isPrimitive()) { | 101 | Type.Primitive argCls = argType.getPrimitive(); |
| 102 | Type.Primitive argCls = argType.getPrimitive(); | 102 | argName = "a" + argCls.name() + (argi + 1); |
| 103 | argName = "a" + argCls.name() + (argi + 1); | 103 | } else if (argType.isArray()) { |
| 104 | } else if (argType.isArray()) { | 104 | // List types would require this whole block again, so just go with aListx |
| 105 | // List types would require this whole block again, so just go with aListx | 105 | argName = "aList" + (argi + 1); |
| 106 | argName = "aList" + (argi + 1); | 106 | } else if (argType.isClass()) { |
| 107 | } else if (argType.isClass()) { | 107 | ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); |
| 108 | ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); | 108 | argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1); |
| 109 | argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argi + 1); | 109 | } else { |
| 110 | } else { | 110 | argName = "a" + (argi + 1); |
| 111 | argName = "a" + (argi + 1); | 111 | } |
| 112 | } | 112 | } |
| 113 | } | 113 | renameVariable(table, i, constants.addUtf8Info(argName)); |
| 114 | renameVariable(table, i, constants.addUtf8Info(argName)); | 114 | } |
| 115 | } | 115 | } |
| 116 | } | 116 | |
| 117 | 117 | // then rename the rest of the args, if any | |
| 118 | // then rename the rest of the args, if any | 118 | for (int i = starti + numArgs; i < table.tableLength(); i++) { |
| 119 | for (int i = starti + numArgs; i < table.tableLength(); i++) { | 119 | int firstIndex = table.index(starti + numArgs); |
| 120 | int firstIndex = table.index(starti + numArgs); | 120 | renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); |
| 121 | renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); | 121 | } |
| 122 | } | 122 | } |
| 123 | } | 123 | |
| 124 | 124 | private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { | |
| 125 | private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { | 125 | // rename args to the same names as in the LVT |
| 126 | // rename args to the same names as in the LVT | 126 | for (int i = 0; i < typeTable.tableLength(); i++) { |
| 127 | for (int i = 0; i < typeTable.tableLength(); i++) { | 127 | renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); |
| 128 | renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); | 128 | } |
| 129 | } | 129 | } |
| 130 | } | 130 | |
| 131 | 131 | private void renameVariable(LocalVariableAttribute table, int i, int stringId) { | |
| 132 | private void renameVariable(LocalVariableAttribute table, int i, int stringId) { | 132 | // based off of LocalVariableAttribute.nameIndex() |
| 133 | // based off of LocalVariableAttribute.nameIndex() | 133 | ByteArray.write16bit(stringId, table.get(), i * 10 + 6); |
| 134 | ByteArray.write16bit(stringId, table.get(), i * 10 + 6); | 134 | } |
| 135 | } | 135 | |
| 136 | 136 | private int getNameIndex(LocalVariableAttribute table, int index) { | |
| 137 | private int getNameIndex(LocalVariableAttribute table, int index) { | 137 | for (int i = 0; i < table.tableLength(); i++) { |
| 138 | for (int i = 0; i < table.tableLength(); i++) { | 138 | if (table.index(i) == index) { |
| 139 | if (table.index(i) == index) { | 139 | return table.nameIndex(i); |
| 140 | return table.nameIndex(i); | 140 | } |
| 141 | } | 141 | } |
| 142 | } | 142 | return 0; |
| 143 | return 0; | 143 | } |
| 144 | } | ||
| 145 | } | 144 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java index 28ad04ad..d63572e9 100644 --- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java | |||
| @@ -8,10 +8,8 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | 11 | ||
| 13 | import java.util.ArrayList; | 12 | package cuchaz.enigma.bytecode; |
| 14 | import java.util.List; | ||
| 15 | 13 | ||
| 16 | import cuchaz.enigma.mapping.*; | 14 | import cuchaz.enigma.mapping.*; |
| 17 | import javassist.CtBehavior; | 15 | import javassist.CtBehavior; |
| @@ -19,48 +17,51 @@ import javassist.CtClass; | |||
| 19 | import javassist.bytecode.CodeAttribute; | 17 | import javassist.bytecode.CodeAttribute; |
| 20 | import javassist.bytecode.LocalVariableAttribute; | 18 | import javassist.bytecode.LocalVariableAttribute; |
| 21 | 19 | ||
| 20 | import java.util.ArrayList; | ||
| 21 | import java.util.List; | ||
| 22 | |||
| 22 | public class MethodParameterWriter { | 23 | public class MethodParameterWriter { |
| 23 | 24 | ||
| 24 | private Translator translator; | 25 | private Translator translator; |
| 25 | 26 | ||
| 26 | public MethodParameterWriter(Translator translator) { | 27 | public MethodParameterWriter(Translator translator) { |
| 27 | this.translator = translator; | 28 | this.translator = translator; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | public void writeMethodArguments(CtClass c) { | 31 | public void writeMethodArguments(CtClass c) { |
| 31 | 32 | ||
| 32 | // Procyon will read method arguments from the "MethodParameters" attribute, so write those | 33 | // Procyon will read method arguments from the "MethodParameters" attribute, so write those |
| 33 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 34 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 34 | 35 | ||
| 35 | // if there's a local variable table here, don't write a MethodParameters attribute | 36 | // if there's a local variable table here, don't write a MethodParameters attribute |
| 36 | // let the local variable writer deal with it instead | 37 | // let the local variable writer deal with it instead |
| 37 | // procyon starts doing really weird things if we give it both attributes | 38 | // procyon starts doing really weird things if we give it both attributes |
| 38 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); | 39 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); |
| 39 | if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { | 40 | if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { |
| 40 | continue; | 41 | continue; |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 44 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 44 | 45 | ||
| 45 | // get the number of arguments | 46 | // get the number of arguments |
| 46 | Signature signature = behaviorEntry.getSignature(); | 47 | Signature signature = behaviorEntry.getSignature(); |
| 47 | if (signature == null) { | 48 | if (signature == null) { |
| 48 | // static initializers have no signatures, or arguments | 49 | // static initializers have no signatures, or arguments |
| 49 | continue; | 50 | continue; |
| 50 | } | 51 | } |
| 51 | int numParams = signature.getArgumentTypes().size(); | 52 | int numParams = signature.getArgumentTypes().size(); |
| 52 | if (numParams <= 0) { | 53 | if (numParams <= 0) { |
| 53 | continue; | 54 | continue; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | // get the list of argument names | 57 | // get the list of argument names |
| 57 | List<String> names = new ArrayList<>(numParams); | 58 | List<String> names = new ArrayList<>(numParams); |
| 58 | for (int i = 0; i < numParams; i++) { | 59 | for (int i = 0; i < numParams; i++) { |
| 59 | names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); | 60 | names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | // save the mappings to the class | 63 | // save the mappings to the class |
| 63 | MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); | 64 | MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); |
| 64 | } | 65 | } |
| 65 | } | 66 | } |
| 66 | } | 67 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java index bace3a0d..3f819abd 100644 --- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java | |||
| @@ -8,79 +8,80 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 12 | 13 | ||
| 14 | import javassist.bytecode.AttributeInfo; | ||
| 15 | import javassist.bytecode.ConstPool; | ||
| 16 | import javassist.bytecode.MethodInfo; | ||
| 17 | |||
| 13 | import java.io.ByteArrayOutputStream; | 18 | import java.io.ByteArrayOutputStream; |
| 14 | import java.io.DataOutputStream; | 19 | import java.io.DataOutputStream; |
| 15 | import java.io.IOException; | 20 | import java.io.IOException; |
| 16 | import java.util.ArrayList; | 21 | import java.util.ArrayList; |
| 17 | import java.util.List; | 22 | import java.util.List; |
| 18 | 23 | ||
| 19 | import javassist.bytecode.AttributeInfo; | ||
| 20 | import javassist.bytecode.ConstPool; | ||
| 21 | import javassist.bytecode.MethodInfo; | ||
| 22 | |||
| 23 | public class MethodParametersAttribute extends AttributeInfo { | 24 | public class MethodParametersAttribute extends AttributeInfo { |
| 24 | 25 | ||
| 25 | private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) { | 26 | private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) { |
| 26 | super(pool, "MethodParameters", writeStruct(parameterNameIndices)); | 27 | super(pool, "MethodParameters", writeStruct(parameterNameIndices)); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | public static void updateClass(MethodInfo info, List<String> names) { | 30 | public static void updateClass(MethodInfo info, List<String> names) { |
| 30 | 31 | ||
| 31 | // add the names to the class const pool | 32 | // add the names to the class const pool |
| 32 | ConstPool constPool = info.getConstPool(); | 33 | ConstPool constPool = info.getConstPool(); |
| 33 | List<Integer> parameterNameIndices = new ArrayList<>(); | 34 | List<Integer> parameterNameIndices = new ArrayList<>(); |
| 34 | for (String name : names) { | 35 | for (String name : names) { |
| 35 | if (name != null) { | 36 | if (name != null) { |
| 36 | parameterNameIndices.add(constPool.addUtf8Info(name)); | 37 | parameterNameIndices.add(constPool.addUtf8Info(name)); |
| 37 | } else { | 38 | } else { |
| 38 | parameterNameIndices.add(0); | 39 | parameterNameIndices.add(0); |
| 39 | } | 40 | } |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | // add the attribute to the method | 43 | // add the attribute to the method |
| 43 | info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); | 44 | info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | private static byte[] writeStruct(List<Integer> parameterNameIndices) { | 47 | private static byte[] writeStruct(List<Integer> parameterNameIndices) { |
| 47 | // JVM 8 Spec says the struct looks like this: | 48 | // JVM 8 Spec says the struct looks like this: |
| 48 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 | 49 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 |
| 49 | // uint8 num_params | 50 | // uint8 num_params |
| 50 | // for each param: | 51 | // for each param: |
| 51 | // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry | 52 | // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry |
| 52 | // uint16 access_flags -> don't care, just set to 0 | 53 | // uint16 access_flags -> don't care, just set to 0 |
| 53 | 54 | ||
| 54 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | 55 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); |
| 55 | DataOutputStream out = new DataOutputStream(buf); | 56 | DataOutputStream out = new DataOutputStream(buf); |
| 56 | 57 | ||
| 57 | // NOTE: java hates unsigned integers, so we have to be careful here | 58 | // NOTE: java hates unsigned integers, so we have to be careful here |
| 58 | // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument | 59 | // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument |
| 59 | // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte | 60 | // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte |
| 60 | // if the int is out of range, the byte stream won't look the way we want and weird things will happen | 61 | // if the int is out of range, the byte stream won't look the way we want and weird things will happen |
| 61 | final int SIZEOF_UINT8 = 1; | 62 | final int SIZEOF_UINT8 = 1; |
| 62 | final int SIZEOF_UINT16 = 2; | 63 | final int SIZEOF_UINT16 = 2; |
| 63 | final int MAX_UINT8 = (1 << 8) - 1; | 64 | final int MAX_UINT8 = (1 << 8) - 1; |
| 64 | final int MAX_UINT16 = (1 << 16) - 1; | 65 | final int MAX_UINT16 = (1 << 16) - 1; |
| 65 | 66 | ||
| 66 | try { | 67 | try { |
| 67 | assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); | 68 | assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); |
| 68 | out.writeByte(parameterNameIndices.size()); | 69 | out.writeByte(parameterNameIndices.size()); |
| 69 | 70 | ||
| 70 | for (Integer index : parameterNameIndices) { | 71 | for (Integer index : parameterNameIndices) { |
| 71 | assert (index >= 0 && index <= MAX_UINT16); | 72 | assert (index >= 0 && index <= MAX_UINT16); |
| 72 | out.writeShort(index); | 73 | out.writeShort(index); |
| 73 | 74 | ||
| 74 | // just write 0 for the access flags | 75 | // just write 0 for the access flags |
| 75 | out.writeShort(0); | 76 | out.writeShort(0); |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | out.close(); | 79 | out.close(); |
| 79 | byte[] data = buf.toByteArray(); | 80 | byte[] data = buf.toByteArray(); |
| 80 | assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); | 81 | assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); |
| 81 | return data; | 82 | return data; |
| 82 | } catch (IOException ex) { | 83 | } catch (IOException ex) { |
| 83 | throw new Error(ex); | 84 | throw new Error(ex); |
| 84 | } | 85 | } |
| 85 | } | 86 | } |
| 86 | } | 87 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java index 66f22839..eaa6e901 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java | |||
| @@ -8,48 +8,49 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class ClassInfoAccessor { | 16 | public class ClassInfoAccessor { |
| 16 | 17 | ||
| 17 | private Object item; | 18 | private static Class<?> clazz; |
| 18 | 19 | private static Field nameIndex; | |
| 19 | private static Class<?> clazz; | 20 | |
| 20 | private static Field nameIndex; | 21 | static { |
| 21 | 22 | try { | |
| 22 | public ClassInfoAccessor(Object item) { | 23 | clazz = Class.forName("javassist.bytecode.ClassInfo"); |
| 23 | this.item = item; | 24 | nameIndex = clazz.getDeclaredField("name"); |
| 24 | } | 25 | nameIndex.setAccessible(true); |
| 25 | 26 | } catch (Exception ex) { | |
| 26 | public int getNameIndex() { | 27 | throw new Error(ex); |
| 27 | try { | 28 | } |
| 28 | return (Integer) nameIndex.get(this.item); | 29 | } |
| 29 | } catch (Exception ex) { | 30 | |
| 30 | throw new Error(ex); | 31 | private Object item; |
| 31 | } | 32 | |
| 32 | } | 33 | public ClassInfoAccessor(Object item) { |
| 33 | 34 | this.item = item; | |
| 34 | public void setNameIndex(int val) { | 35 | } |
| 35 | try { | 36 | |
| 36 | nameIndex.set(this.item, val); | 37 | public static boolean isType(ConstInfoAccessor accessor) { |
| 37 | } catch (Exception ex) { | 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 38 | throw new Error(ex); | 39 | } |
| 39 | } | 40 | |
| 40 | } | 41 | public int getNameIndex() { |
| 41 | 42 | try { | |
| 42 | public static boolean isType(ConstInfoAccessor accessor) { | 43 | return (Integer) nameIndex.get(this.item); |
| 43 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 44 | } catch (Exception ex) { |
| 44 | } | 45 | throw new Error(ex); |
| 45 | 46 | } | |
| 46 | static { | 47 | } |
| 47 | try { | 48 | |
| 48 | clazz = Class.forName("javassist.bytecode.ClassInfo"); | 49 | public void setNameIndex(int val) { |
| 49 | nameIndex = clazz.getDeclaredField("name"); | 50 | try { |
| 50 | nameIndex.setAccessible(true); | 51 | nameIndex.set(this.item, val); |
| 51 | } catch (Exception ex) { | 52 | } catch (Exception ex) { |
| 52 | throw new Error(ex); | 53 | throw new Error(ex); |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 55 | } | 56 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java index aa363d2a..27d991a3 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java | |||
| @@ -8,122 +8,117 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | 11 | ||
| 13 | import java.io.ByteArrayInputStream; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 14 | import java.io.ByteArrayOutputStream; | ||
| 15 | import java.io.DataInputStream; | ||
| 16 | import java.io.DataOutputStream; | ||
| 17 | import java.io.IOException; | ||
| 18 | import java.io.OutputStreamWriter; | ||
| 19 | import java.io.PrintWriter; | ||
| 20 | import java.lang.reflect.Field; | ||
| 21 | import java.lang.reflect.Method; | ||
| 22 | 13 | ||
| 23 | import com.google.common.base.Charsets; | 14 | import com.google.common.base.Charsets; |
| 24 | import cuchaz.enigma.bytecode.InfoType; | 15 | import cuchaz.enigma.bytecode.InfoType; |
| 25 | 16 | ||
| 17 | import java.io.*; | ||
| 18 | import java.lang.reflect.Field; | ||
| 19 | import java.lang.reflect.Method; | ||
| 20 | |||
| 26 | public class ConstInfoAccessor { | 21 | public class ConstInfoAccessor { |
| 27 | 22 | ||
| 28 | private static Class<?> clazz; | 23 | private static Class<?> clazz; |
| 29 | private static Field index; | 24 | private static Field index; |
| 30 | private static Method getTag; | 25 | private static Method getTag; |
| 31 | 26 | ||
| 32 | private Object item; | 27 | static { |
| 33 | 28 | try { | |
| 34 | public ConstInfoAccessor(Object item) { | 29 | clazz = Class.forName("javassist.bytecode.ConstInfo"); |
| 35 | if (item == null) { | 30 | index = clazz.getDeclaredField("index"); |
| 36 | throw new IllegalArgumentException("item cannot be null!"); | 31 | index.setAccessible(true); |
| 37 | } | 32 | getTag = clazz.getMethod("getTag"); |
| 38 | this.item = item; | 33 | getTag.setAccessible(true); |
| 39 | } | 34 | } catch (Exception ex) { |
| 40 | 35 | throw new Error(ex); | |
| 41 | public Object getItem() { | 36 | } |
| 42 | return this.item; | 37 | } |
| 43 | } | 38 | |
| 44 | 39 | private Object item; | |
| 45 | public int getIndex() { | 40 | |
| 46 | try { | 41 | public ConstInfoAccessor(Object item) { |
| 47 | return (Integer) index.get(this.item); | 42 | if (item == null) { |
| 48 | } catch (Exception ex) { | 43 | throw new IllegalArgumentException("item cannot be null!"); |
| 49 | throw new Error(ex); | 44 | } |
| 50 | } | 45 | this.item = item; |
| 51 | } | 46 | } |
| 52 | 47 | ||
| 53 | public int getTag() { | 48 | public Object getItem() { |
| 54 | try { | 49 | return this.item; |
| 55 | return (Integer) getTag.invoke(this.item); | 50 | } |
| 56 | } catch (Exception ex) { | 51 | |
| 57 | throw new Error(ex); | 52 | public int getIndex() { |
| 58 | } | 53 | try { |
| 59 | } | 54 | return (Integer) index.get(this.item); |
| 60 | 55 | } catch (Exception ex) { | |
| 61 | public ConstInfoAccessor copy() { | 56 | throw new Error(ex); |
| 62 | return new ConstInfoAccessor(copyItem()); | 57 | } |
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | public Object copyItem() { | 60 | public int getTag() { |
| 66 | // I don't know of a simpler way to copy one of these silly things... | 61 | try { |
| 67 | try { | 62 | return (Integer) getTag.invoke(this.item); |
| 68 | // serialize the item | 63 | } catch (Exception ex) { |
| 69 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | 64 | throw new Error(ex); |
| 70 | DataOutputStream out = new DataOutputStream(buf); | 65 | } |
| 71 | write(out); | 66 | } |
| 72 | 67 | ||
| 73 | // deserialize the item | 68 | public ConstInfoAccessor copy() { |
| 74 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); | 69 | return new ConstInfoAccessor(copyItem()); |
| 75 | Object item = new ConstInfoAccessor(in).getItem(); | 70 | } |
| 76 | in.close(); | 71 | |
| 77 | 72 | public Object copyItem() { | |
| 78 | return item; | 73 | // I don't know of a simpler way to copy one of these silly things... |
| 79 | } catch (Exception ex) { | 74 | try { |
| 80 | throw new Error(ex); | 75 | // serialize the item |
| 81 | } | 76 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); |
| 82 | } | 77 | DataOutputStream out = new DataOutputStream(buf); |
| 83 | 78 | write(out); | |
| 84 | public void write(DataOutputStream out) throws IOException { | 79 | |
| 85 | try { | 80 | // deserialize the item |
| 86 | out.writeUTF(this.item.getClass().getName()); | 81 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); |
| 87 | out.writeInt(getIndex()); | 82 | Object item = new ConstInfoAccessor(in).getItem(); |
| 88 | 83 | in.close(); | |
| 89 | Method method = this.item.getClass().getMethod("write", DataOutputStream.class); | 84 | |
| 90 | method.setAccessible(true); | 85 | return item; |
| 91 | method.invoke(this.item, out); | 86 | } catch (Exception ex) { |
| 92 | } catch (IOException ex) { | 87 | throw new Error(ex); |
| 93 | throw ex; | 88 | } |
| 94 | } catch (Exception ex) { | 89 | } |
| 95 | throw new Error(ex); | 90 | |
| 96 | } | 91 | public void write(DataOutputStream out) throws IOException { |
| 97 | } | 92 | try { |
| 98 | 93 | out.writeUTF(this.item.getClass().getName()); | |
| 99 | @Override | 94 | out.writeInt(getIndex()); |
| 100 | public String toString() { | 95 | |
| 101 | try { | 96 | Method method = this.item.getClass().getMethod("write", DataOutputStream.class); |
| 102 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | 97 | method.setAccessible(true); |
| 103 | PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); | 98 | method.invoke(this.item, out); |
| 104 | Method print = this.item.getClass().getMethod("print", PrintWriter.class); | 99 | } catch (IOException ex) { |
| 105 | print.setAccessible(true); | 100 | throw ex; |
| 106 | print.invoke(this.item, out); | 101 | } catch (Exception ex) { |
| 107 | out.close(); | 102 | throw new Error(ex); |
| 108 | return buf.toString("UTF-8").replace("\n", ""); | 103 | } |
| 109 | } catch (Exception ex) { | 104 | } |
| 110 | throw new Error(ex); | 105 | |
| 111 | } | 106 | @Override |
| 112 | } | 107 | public String toString() { |
| 113 | 108 | try { | |
| 114 | public InfoType getType() { | 109 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); |
| 115 | return InfoType.getByTag(getTag()); | 110 | PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); |
| 116 | } | 111 | Method print = this.item.getClass().getMethod("print", PrintWriter.class); |
| 117 | 112 | print.setAccessible(true); | |
| 118 | static { | 113 | print.invoke(this.item, out); |
| 119 | try { | 114 | out.close(); |
| 120 | clazz = Class.forName("javassist.bytecode.ConstInfo"); | 115 | return buf.toString("UTF-8").replace("\n", ""); |
| 121 | index = clazz.getDeclaredField("index"); | 116 | } catch (Exception ex) { |
| 122 | index.setAccessible(true); | 117 | throw new Error(ex); |
| 123 | getTag = clazz.getMethod("getTag"); | 118 | } |
| 124 | getTag.setAccessible(true); | 119 | } |
| 125 | } catch (Exception ex) { | 120 | |
| 126 | throw new Error(ex); | 121 | public InfoType getType() { |
| 127 | } | 122 | return InfoType.getByTag(getTag()); |
| 128 | } | 123 | } |
| 129 | } | 124 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java index 69aee160..aef35321 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java | |||
| @@ -8,68 +8,68 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class InvokeDynamicInfoAccessor { | 16 | public class InvokeDynamicInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field bootstrapIndex; | 19 | private static Field bootstrapIndex; |
| 19 | private static Field nameAndTypeIndex; | 20 | private static Field nameAndTypeIndex; |
| 20 | |||
| 21 | 21 | ||
| 22 | private Object item; | 22 | static { |
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); | ||
| 25 | bootstrapIndex = clazz.getDeclaredField("bootstrap"); | ||
| 26 | bootstrapIndex.setAccessible(true); | ||
| 27 | nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); | ||
| 28 | nameAndTypeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 23 | 33 | ||
| 24 | public InvokeDynamicInfoAccessor(Object item) { | 34 | private Object item; |
| 25 | this.item = item; | ||
| 26 | } | ||
| 27 | 35 | ||
| 28 | public int getBootstrapIndex() { | 36 | public InvokeDynamicInfoAccessor(Object item) { |
| 29 | try { | 37 | this.item = item; |
| 30 | return (Integer) bootstrapIndex.get(this.item); | 38 | } |
| 31 | } catch (Exception ex) { | ||
| 32 | throw new Error(ex); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | 39 | ||
| 36 | public void setBootstrapIndex(int val) { | 40 | public static boolean isType(ConstInfoAccessor accessor) { |
| 37 | try { | 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 38 | bootstrapIndex.set(this.item, val); | 42 | } |
| 39 | } catch (Exception ex) { | ||
| 40 | throw new Error(ex); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | 43 | ||
| 44 | public int getNameAndTypeIndex() { | 44 | public int getBootstrapIndex() { |
| 45 | try { | 45 | try { |
| 46 | return (Integer) nameAndTypeIndex.get(this.item); | 46 | return (Integer) bootstrapIndex.get(this.item); |
| 47 | } catch (Exception ex) { | 47 | } catch (Exception ex) { |
| 48 | throw new Error(ex); | 48 | throw new Error(ex); |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | public void setNameAndTypeIndex(int val) { | 52 | public void setBootstrapIndex(int val) { |
| 53 | try { | 53 | try { |
| 54 | nameAndTypeIndex.set(this.item, val); | 54 | bootstrapIndex.set(this.item, val); |
| 55 | } catch (Exception ex) { | 55 | } catch (Exception ex) { |
| 56 | throw new Error(ex); | 56 | throw new Error(ex); |
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | public static boolean isType(ConstInfoAccessor accessor) { | 60 | public int getNameAndTypeIndex() { |
| 61 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 61 | try { |
| 62 | } | 62 | return (Integer) nameAndTypeIndex.get(this.item); |
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 63 | 67 | ||
| 64 | static { | 68 | public void setNameAndTypeIndex(int val) { |
| 65 | try { | 69 | try { |
| 66 | clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); | 70 | nameAndTypeIndex.set(this.item, val); |
| 67 | bootstrapIndex = clazz.getDeclaredField("bootstrap"); | 71 | } catch (Exception ex) { |
| 68 | bootstrapIndex.setAccessible(true); | 72 | throw new Error(ex); |
| 69 | nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); | 73 | } |
| 70 | nameAndTypeIndex.setAccessible(true); | 74 | } |
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | 75 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java index 0e0297be..058bb454 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java | |||
| @@ -8,67 +8,68 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class MemberRefInfoAccessor { | 16 | public class MemberRefInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field classIndex; | 19 | private static Field classIndex; |
| 19 | private static Field nameAndTypeIndex; | 20 | private static Field nameAndTypeIndex; |
| 20 | 21 | ||
| 21 | private Object item; | 22 | static { |
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.MemberrefInfo"); | ||
| 25 | classIndex = clazz.getDeclaredField("classIndex"); | ||
| 26 | classIndex.setAccessible(true); | ||
| 27 | nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); | ||
| 28 | nameAndTypeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 22 | 33 | ||
| 23 | public MemberRefInfoAccessor(Object item) { | 34 | private Object item; |
| 24 | this.item = item; | ||
| 25 | } | ||
| 26 | 35 | ||
| 27 | public int getClassIndex() { | 36 | public MemberRefInfoAccessor(Object item) { |
| 28 | try { | 37 | this.item = item; |
| 29 | return (Integer) classIndex.get(this.item); | 38 | } |
| 30 | } catch (Exception ex) { | ||
| 31 | throw new Error(ex); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | 39 | ||
| 35 | public void setClassIndex(int val) { | 40 | public static boolean isType(ConstInfoAccessor accessor) { |
| 36 | try { | 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 37 | classIndex.set(this.item, val); | 42 | } |
| 38 | } catch (Exception ex) { | ||
| 39 | throw new Error(ex); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | 43 | ||
| 43 | public int getNameAndTypeIndex() { | 44 | public int getClassIndex() { |
| 44 | try { | 45 | try { |
| 45 | return (Integer) nameAndTypeIndex.get(this.item); | 46 | return (Integer) classIndex.get(this.item); |
| 46 | } catch (Exception ex) { | 47 | } catch (Exception ex) { |
| 47 | throw new Error(ex); | 48 | throw new Error(ex); |
| 48 | } | 49 | } |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | public void setNameAndTypeIndex(int val) { | 52 | public void setClassIndex(int val) { |
| 52 | try { | 53 | try { |
| 53 | nameAndTypeIndex.set(this.item, val); | 54 | classIndex.set(this.item, val); |
| 54 | } catch (Exception ex) { | 55 | } catch (Exception ex) { |
| 55 | throw new Error(ex); | 56 | throw new Error(ex); |
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | public static boolean isType(ConstInfoAccessor accessor) { | 60 | public int getNameAndTypeIndex() { |
| 60 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 61 | try { |
| 61 | } | 62 | return (Integer) nameAndTypeIndex.get(this.item); |
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 62 | 67 | ||
| 63 | static { | 68 | public void setNameAndTypeIndex(int val) { |
| 64 | try { | 69 | try { |
| 65 | clazz = Class.forName("javassist.bytecode.MemberrefInfo"); | 70 | nameAndTypeIndex.set(this.item, val); |
| 66 | classIndex = clazz.getDeclaredField("classIndex"); | 71 | } catch (Exception ex) { |
| 67 | classIndex.setAccessible(true); | 72 | throw new Error(ex); |
| 68 | nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); | 73 | } |
| 69 | nameAndTypeIndex.setAccessible(true); | 74 | } |
| 70 | } catch (Exception ex) { | ||
| 71 | throw new Error(ex); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | 75 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java index 9a7dd698..985e792e 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java | |||
| @@ -8,67 +8,68 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class MethodHandleInfoAccessor { | 16 | public class MethodHandleInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field kindIndex; | 19 | private static Field kindIndex; |
| 19 | private static Field indexIndex; | 20 | private static Field indexIndex; |
| 20 | 21 | ||
| 21 | private Object item; | 22 | static { |
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); | ||
| 25 | kindIndex = clazz.getDeclaredField("refKind"); | ||
| 26 | kindIndex.setAccessible(true); | ||
| 27 | indexIndex = clazz.getDeclaredField("refIndex"); | ||
| 28 | indexIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 22 | 33 | ||
| 23 | public MethodHandleInfoAccessor(Object item) { | 34 | private Object item; |
| 24 | this.item = item; | ||
| 25 | } | ||
| 26 | 35 | ||
| 27 | public int getTypeIndex() { | 36 | public MethodHandleInfoAccessor(Object item) { |
| 28 | try { | 37 | this.item = item; |
| 29 | return (Integer) kindIndex.get(this.item); | 38 | } |
| 30 | } catch (Exception ex) { | ||
| 31 | throw new Error(ex); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | 39 | ||
| 35 | public void setTypeIndex(int val) { | 40 | public static boolean isType(ConstInfoAccessor accessor) { |
| 36 | try { | 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 37 | kindIndex.set(this.item, val); | 42 | } |
| 38 | } catch (Exception ex) { | ||
| 39 | throw new Error(ex); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | 43 | ||
| 43 | public int getMethodRefIndex() { | 44 | public int getTypeIndex() { |
| 44 | try { | 45 | try { |
| 45 | return (Integer) indexIndex.get(this.item); | 46 | return (Integer) kindIndex.get(this.item); |
| 46 | } catch (Exception ex) { | 47 | } catch (Exception ex) { |
| 47 | throw new Error(ex); | 48 | throw new Error(ex); |
| 48 | } | 49 | } |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | public void setMethodRefIndex(int val) { | 52 | public void setTypeIndex(int val) { |
| 52 | try { | 53 | try { |
| 53 | indexIndex.set(this.item, val); | 54 | kindIndex.set(this.item, val); |
| 54 | } catch (Exception ex) { | 55 | } catch (Exception ex) { |
| 55 | throw new Error(ex); | 56 | throw new Error(ex); |
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | public static boolean isType(ConstInfoAccessor accessor) { | 60 | public int getMethodRefIndex() { |
| 60 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 61 | try { |
| 61 | } | 62 | return (Integer) indexIndex.get(this.item); |
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 62 | 67 | ||
| 63 | static { | 68 | public void setMethodRefIndex(int val) { |
| 64 | try { | 69 | try { |
| 65 | clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); | 70 | indexIndex.set(this.item, val); |
| 66 | kindIndex = clazz.getDeclaredField("refKind"); | 71 | } catch (Exception ex) { |
| 67 | kindIndex.setAccessible(true); | 72 | throw new Error(ex); |
| 68 | indexIndex = clazz.getDeclaredField("refIndex"); | 73 | } |
| 69 | indexIndex.setAccessible(true); | 74 | } |
| 70 | } catch (Exception ex) { | ||
| 71 | throw new Error(ex); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | 75 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java index 5ec9c3b4..10b0cb0c 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java | |||
| @@ -8,49 +8,50 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class MethodTypeInfoAccessor { | 16 | public class MethodTypeInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field descriptorIndex; | 19 | private static Field descriptorIndex; |
| 19 | 20 | ||
| 20 | private Object item; | 21 | static { |
| 21 | 22 | try { | |
| 22 | public MethodTypeInfoAccessor(Object item) { | 23 | clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); |
| 23 | this.item = item; | 24 | descriptorIndex = clazz.getDeclaredField("descriptor"); |
| 24 | } | 25 | descriptorIndex.setAccessible(true); |
| 25 | 26 | } catch (Exception ex) { | |
| 26 | public int getTypeIndex() { | 27 | throw new Error(ex); |
| 27 | try { | 28 | } |
| 28 | return (Integer) descriptorIndex.get(this.item); | 29 | } |
| 29 | } catch (Exception ex) { | 30 | |
| 30 | throw new Error(ex); | 31 | private Object item; |
| 31 | } | 32 | |
| 32 | } | 33 | public MethodTypeInfoAccessor(Object item) { |
| 33 | 34 | this.item = item; | |
| 34 | public void setTypeIndex(int val) { | 35 | } |
| 35 | try { | 36 | |
| 36 | descriptorIndex.set(this.item, val); | 37 | public static boolean isType(ConstInfoAccessor accessor) { |
| 37 | } catch (Exception ex) { | 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 38 | throw new Error(ex); | 39 | } |
| 39 | } | 40 | |
| 40 | } | 41 | public int getTypeIndex() { |
| 41 | 42 | try { | |
| 42 | public static boolean isType(ConstInfoAccessor accessor) { | 43 | return (Integer) descriptorIndex.get(this.item); |
| 43 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 44 | } catch (Exception ex) { |
| 44 | } | 45 | throw new Error(ex); |
| 45 | 46 | } | |
| 46 | static { | 47 | } |
| 47 | try { | 48 | |
| 48 | clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); | 49 | public void setTypeIndex(int val) { |
| 49 | descriptorIndex = clazz.getDeclaredField("descriptor"); | 50 | try { |
| 50 | descriptorIndex.setAccessible(true); | 51 | descriptorIndex.set(this.item, val); |
| 51 | } catch (Exception ex) { | 52 | } catch (Exception ex) { |
| 52 | throw new Error(ex); | 53 | throw new Error(ex); |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | } | 57 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java index 95df37c1..cc7fdbe8 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java | |||
| @@ -8,67 +8,68 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class NameAndTypeInfoAccessor { | 16 | public class NameAndTypeInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field nameIndex; | 19 | private static Field nameIndex; |
| 19 | private static Field typeIndex; | 20 | private static Field typeIndex; |
| 20 | 21 | ||
| 21 | private Object item; | 22 | static { |
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); | ||
| 25 | nameIndex = clazz.getDeclaredField("memberName"); | ||
| 26 | nameIndex.setAccessible(true); | ||
| 27 | typeIndex = clazz.getDeclaredField("typeDescriptor"); | ||
| 28 | typeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 22 | 33 | ||
| 23 | public NameAndTypeInfoAccessor(Object item) { | 34 | private Object item; |
| 24 | this.item = item; | ||
| 25 | } | ||
| 26 | 35 | ||
| 27 | public int getNameIndex() { | 36 | public NameAndTypeInfoAccessor(Object item) { |
| 28 | try { | 37 | this.item = item; |
| 29 | return (Integer) nameIndex.get(this.item); | 38 | } |
| 30 | } catch (Exception ex) { | ||
| 31 | throw new Error(ex); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | 39 | ||
| 35 | public void setNameIndex(int val) { | 40 | public static boolean isType(ConstInfoAccessor accessor) { |
| 36 | try { | 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 37 | nameIndex.set(this.item, val); | 42 | } |
| 38 | } catch (Exception ex) { | ||
| 39 | throw new Error(ex); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | 43 | ||
| 43 | public int getTypeIndex() { | 44 | public int getNameIndex() { |
| 44 | try { | 45 | try { |
| 45 | return (Integer) typeIndex.get(this.item); | 46 | return (Integer) nameIndex.get(this.item); |
| 46 | } catch (Exception ex) { | 47 | } catch (Exception ex) { |
| 47 | throw new Error(ex); | 48 | throw new Error(ex); |
| 48 | } | 49 | } |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | public void setTypeIndex(int val) { | 52 | public void setNameIndex(int val) { |
| 52 | try { | 53 | try { |
| 53 | typeIndex.set(this.item, val); | 54 | nameIndex.set(this.item, val); |
| 54 | } catch (Exception ex) { | 55 | } catch (Exception ex) { |
| 55 | throw new Error(ex); | 56 | throw new Error(ex); |
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | public static boolean isType(ConstInfoAccessor accessor) { | 60 | public int getTypeIndex() { |
| 60 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 61 | try { |
| 61 | } | 62 | return (Integer) typeIndex.get(this.item); |
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 62 | 67 | ||
| 63 | static { | 68 | public void setTypeIndex(int val) { |
| 64 | try { | 69 | try { |
| 65 | clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); | 70 | typeIndex.set(this.item, val); |
| 66 | nameIndex = clazz.getDeclaredField("memberName"); | 71 | } catch (Exception ex) { |
| 67 | nameIndex.setAccessible(true); | 72 | throw new Error(ex); |
| 68 | typeIndex = clazz.getDeclaredField("typeDescriptor"); | 73 | } |
| 69 | typeIndex.setAccessible(true); | 74 | } |
| 70 | } catch (Exception ex) { | ||
| 71 | throw new Error(ex); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | 75 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java index 1c55a443..5c68d4af 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java | |||
| @@ -8,48 +8,49 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | import java.lang.reflect.Field; | 14 | import java.lang.reflect.Field; |
| 14 | 15 | ||
| 15 | public class StringInfoAccessor { | 16 | public class StringInfoAccessor { |
| 16 | 17 | ||
| 17 | private static Class<?> clazz; | 18 | private static Class<?> clazz; |
| 18 | private static Field stringIndex; | 19 | private static Field stringIndex; |
| 19 | 20 | ||
| 20 | private Object item; | 21 | static { |
| 21 | 22 | try { | |
| 22 | public StringInfoAccessor(Object item) { | 23 | clazz = Class.forName("javassist.bytecode.StringInfo"); |
| 23 | this.item = item; | 24 | stringIndex = clazz.getDeclaredField("string"); |
| 24 | } | 25 | stringIndex.setAccessible(true); |
| 25 | 26 | } catch (Exception ex) { | |
| 26 | public int getStringIndex() { | 27 | throw new Error(ex); |
| 27 | try { | 28 | } |
| 28 | return (Integer) stringIndex.get(this.item); | 29 | } |
| 29 | } catch (Exception ex) { | 30 | |
| 30 | throw new Error(ex); | 31 | private Object item; |
| 31 | } | 32 | |
| 32 | } | 33 | public StringInfoAccessor(Object item) { |
| 33 | 34 | this.item = item; | |
| 34 | public void setStringIndex(int val) { | 35 | } |
| 35 | try { | 36 | |
| 36 | stringIndex.set(this.item, val); | 37 | public static boolean isType(ConstInfoAccessor accessor) { |
| 37 | } catch (Exception ex) { | 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 38 | throw new Error(ex); | 39 | } |
| 39 | } | 40 | |
| 40 | } | 41 | public int getStringIndex() { |
| 41 | 42 | try { | |
| 42 | public static boolean isType(ConstInfoAccessor accessor) { | 43 | return (Integer) stringIndex.get(this.item); |
| 43 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 44 | } catch (Exception ex) { |
| 44 | } | 45 | throw new Error(ex); |
| 45 | 46 | } | |
| 46 | static { | 47 | } |
| 47 | try { | 48 | |
| 48 | clazz = Class.forName("javassist.bytecode.StringInfo"); | 49 | public void setStringIndex(int val) { |
| 49 | stringIndex = clazz.getDeclaredField("string"); | 50 | try { |
| 50 | stringIndex.setAccessible(true); | 51 | stringIndex.set(this.item, val); |
| 51 | } catch (Exception ex) { | 52 | } catch (Exception ex) { |
| 52 | throw new Error(ex); | 53 | throw new Error(ex); |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 55 | } | 56 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java index 7a2cb667..cc3b41bc 100644 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java | |||
| @@ -8,21 +8,22 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.bytecode.accessors; | 12 | package cuchaz.enigma.bytecode.accessors; |
| 12 | 13 | ||
| 13 | public class Utf8InfoAccessor { | 14 | public class Utf8InfoAccessor { |
| 14 | 15 | ||
| 15 | private static Class<?> clazz; | 16 | private static Class<?> clazz; |
| 16 | 17 | ||
| 17 | static { | 18 | static { |
| 18 | try { | 19 | try { |
| 19 | clazz = Class.forName("javassist.bytecode.Utf8Info"); | 20 | clazz = Class.forName("javassist.bytecode.Utf8Info"); |
| 20 | } catch (Exception ex) { | 21 | } catch (Exception ex) { |
| 21 | throw new Error(ex); | 22 | throw new Error(ex); |
| 22 | } | 23 | } |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | public static boolean isType(ConstInfoAccessor accessor) { | 26 | public static boolean isType(ConstInfoAccessor accessor) { |
| 26 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | 27 | return clazz.isAssignableFrom(accessor.getItem().getClass()); |
| 27 | } | 28 | } |
| 28 | } | 29 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassForest.java b/src/main/java/cuchaz/enigma/convert/ClassForest.java index b08d48fb..4542fb33 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassForest.java +++ b/src/main/java/cuchaz/enigma/convert/ClassForest.java | |||
| @@ -8,53 +8,52 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 14 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 15 | |||
| 16 | import java.util.Collection; | ||
| 17 | |||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 16 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | 17 | ||
| 18 | import java.util.Collection; | ||
| 20 | 19 | ||
| 21 | public class ClassForest { | 20 | public class ClassForest { |
| 22 | 21 | ||
| 23 | private ClassIdentifier identifier; | 22 | private ClassIdentifier identifier; |
| 24 | private Multimap<ClassIdentity, ClassEntry> forest; | 23 | private Multimap<ClassIdentity, ClassEntry> forest; |
| 25 | 24 | ||
| 26 | public ClassForest(ClassIdentifier identifier) { | 25 | public ClassForest(ClassIdentifier identifier) { |
| 27 | this.identifier = identifier; | 26 | this.identifier = identifier; |
| 28 | this.forest = HashMultimap.create(); | 27 | this.forest = HashMultimap.create(); |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | public void addAll(Iterable<ClassEntry> entries) { | 30 | public void addAll(Iterable<ClassEntry> entries) { |
| 32 | for (ClassEntry entry : entries) { | 31 | for (ClassEntry entry : entries) { |
| 33 | add(entry); | 32 | add(entry); |
| 34 | } | 33 | } |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | public void add(ClassEntry entry) { | 36 | public void add(ClassEntry entry) { |
| 38 | try { | 37 | try { |
| 39 | this.forest.put(this.identifier.identify(entry), entry); | 38 | this.forest.put(this.identifier.identify(entry), entry); |
| 40 | } catch (ClassNotFoundException ex) { | 39 | } catch (ClassNotFoundException ex) { |
| 41 | throw new Error("Unable to find class " + entry.getName()); | 40 | throw new Error("Unable to find class " + entry.getName()); |
| 42 | } | 41 | } |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | public Collection<ClassIdentity> identities() { | 44 | public Collection<ClassIdentity> identities() { |
| 46 | return this.forest.keySet(); | 45 | return this.forest.keySet(); |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | public Collection<ClassEntry> classes() { | 48 | public Collection<ClassEntry> classes() { |
| 50 | return this.forest.values(); | 49 | return this.forest.values(); |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public Collection<ClassEntry> getClasses(ClassIdentity identity) { | 52 | public Collection<ClassEntry> getClasses(ClassIdentity identity) { |
| 54 | return this.forest.get(identity); | 53 | return this.forest.get(identity); |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | public boolean containsIdentity(ClassIdentity identity) { | 56 | public boolean containsIdentity(ClassIdentity identity) { |
| 58 | return this.forest.containsKey(identity); | 57 | return this.forest.containsKey(identity); |
| 59 | } | 58 | } |
| 60 | } | 59 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java index 557e6083..0a72073c 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java +++ b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java | |||
| @@ -8,13 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 14 | |||
| 15 | import java.util.Map; | ||
| 16 | import java.util.jar.JarFile; | ||
| 17 | |||
| 18 | import cuchaz.enigma.TranslatingTypeLoader; | 15 | import cuchaz.enigma.TranslatingTypeLoader; |
| 19 | import cuchaz.enigma.analysis.JarIndex; | 16 | import cuchaz.enigma.analysis.JarIndex; |
| 20 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 17 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| @@ -22,34 +19,36 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 22 | import cuchaz.enigma.mapping.Translator; | 19 | import cuchaz.enigma.mapping.Translator; |
| 23 | import javassist.CtClass; | 20 | import javassist.CtClass; |
| 24 | 21 | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.jar.JarFile; | ||
| 25 | 24 | ||
| 26 | public class ClassIdentifier { | 25 | public class ClassIdentifier { |
| 27 | 26 | ||
| 28 | private JarIndex index; | 27 | private JarIndex index; |
| 29 | private SidedClassNamer namer; | 28 | private SidedClassNamer namer; |
| 30 | private boolean useReferences; | 29 | private boolean useReferences; |
| 31 | private TranslatingTypeLoader loader; | 30 | private TranslatingTypeLoader loader; |
| 32 | private Map<ClassEntry, ClassIdentity> cache; | 31 | private Map<ClassEntry, ClassIdentity> cache; |
| 33 | 32 | ||
| 34 | public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { | 33 | public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { |
| 35 | this.index = index; | 34 | this.index = index; |
| 36 | this.namer = namer; | 35 | this.namer = namer; |
| 37 | this.useReferences = useReferences; | 36 | this.useReferences = useReferences; |
| 38 | this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); | 37 | this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); |
| 39 | this.cache = Maps.newHashMap(); | 38 | this.cache = Maps.newHashMap(); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | public ClassIdentity identify(ClassEntry classEntry) | 41 | public ClassIdentity identify(ClassEntry classEntry) |
| 43 | throws ClassNotFoundException { | 42 | throws ClassNotFoundException { |
| 44 | ClassIdentity identity = this.cache.get(classEntry); | 43 | ClassIdentity identity = this.cache.get(classEntry); |
| 45 | if (identity == null) { | 44 | if (identity == null) { |
| 46 | CtClass c = this.loader.loadClass(classEntry.getName()); | 45 | CtClass c = this.loader.loadClass(classEntry.getName()); |
| 47 | if (c == null) { | 46 | if (c == null) { |
| 48 | throw new ClassNotFoundException(classEntry.getName()); | 47 | throw new ClassNotFoundException(classEntry.getName()); |
| 49 | } | 48 | } |
| 50 | identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); | 49 | identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); |
| 51 | this.cache.put(classEntry, identity); | 50 | this.cache.put(classEntry, identity); |
| 52 | } | 51 | } |
| 53 | return identity; | 52 | return identity; |
| 54 | } | 53 | } |
| 55 | } | 54 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java index f72bf703..a395b755 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java | |||
| @@ -8,18 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| 14 | |||
| 15 | import java.io.UnsupportedEncodingException; | ||
| 16 | import java.security.MessageDigest; | ||
| 17 | import java.security.NoSuchAlgorithmException; | ||
| 18 | import java.util.Enumeration; | ||
| 19 | import java.util.List; | ||
| 20 | import java.util.Map; | ||
| 21 | import java.util.Set; | ||
| 22 | |||
| 23 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; | 15 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; |
| 24 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 25 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -33,408 +25,415 @@ import javassist.*; | |||
| 33 | import javassist.bytecode.*; | 25 | import javassist.bytecode.*; |
| 34 | import javassist.expr.*; | 26 | import javassist.expr.*; |
| 35 | 27 | ||
| 28 | import java.io.UnsupportedEncodingException; | ||
| 29 | import java.security.MessageDigest; | ||
| 30 | import java.security.NoSuchAlgorithmException; | ||
| 31 | import java.util.Enumeration; | ||
| 32 | import java.util.List; | ||
| 33 | import java.util.Map; | ||
| 34 | import java.util.Set; | ||
| 35 | |||
| 36 | public class ClassIdentity { | 36 | public class ClassIdentity { |
| 37 | 37 | ||
| 38 | private ClassEntry classEntry; | 38 | private ClassEntry classEntry; |
| 39 | private SidedClassNamer namer; | 39 | private SidedClassNamer namer; |
| 40 | private Multiset<String> fields; | 40 | private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { |
| 41 | private Multiset<String> methods; | 41 | |
| 42 | private Multiset<String> constructors; | 42 | private Map<String, String> classNames = Maps.newHashMap(); |
| 43 | private String staticInitializer; | 43 | |
| 44 | private String extendz; | 44 | @Override |
| 45 | private Multiset<String> implementz; | 45 | public String replace(String className) { |
| 46 | private Set<String> stringLiterals; | 46 | |
| 47 | private Multiset<String> implementations; | 47 | // classes not in the none package can be passed through |
| 48 | private Multiset<String> references; | 48 | ClassEntry classEntry = new ClassEntry(className); |
| 49 | private String outer; | 49 | if (classEntry.getPackageName() != null) { |
| 50 | 50 | return className; | |
| 51 | private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { | 51 | } |
| 52 | 52 | ||
| 53 | private Map<String, String> classNames = Maps.newHashMap(); | 53 | // is this class ourself? |
| 54 | 54 | if (className.equals(classEntry.getName())) { | |
| 55 | @Override | 55 | return "CSelf"; |
| 56 | public String replace(String className) { | 56 | } |
| 57 | 57 | ||
| 58 | // classes not in the none package can be passed through | 58 | // try the namer |
| 59 | ClassEntry classEntry = new ClassEntry(className); | 59 | if (namer != null) { |
| 60 | if (classEntry.getPackageName() != null) { | 60 | String newName = namer.getName(className); |
| 61 | return className; | 61 | if (newName != null) { |
| 62 | } | 62 | return newName; |
| 63 | 63 | } | |
| 64 | // is this class ourself? | 64 | } |
| 65 | if (className.equals(classEntry.getName())) { | 65 | |
| 66 | return "CSelf"; | 66 | // otherwise, use local naming |
| 67 | } | 67 | if (!classNames.containsKey(className)) { |
| 68 | 68 | classNames.put(className, getNewClassName()); | |
| 69 | // try the namer | 69 | } |
| 70 | if (namer != null) { | 70 | return classNames.get(className); |
| 71 | String newName = namer.getName(className); | 71 | } |
| 72 | if (newName != null) { | 72 | |
| 73 | return newName; | 73 | private String getNewClassName() { |
| 74 | } | 74 | return String.format("C%03d", classNames.size()); |
| 75 | } | 75 | } |
| 76 | 76 | }; | |
| 77 | // otherwise, use local naming | 77 | private Multiset<String> fields; |
| 78 | if (!classNames.containsKey(className)) { | 78 | private Multiset<String> methods; |
| 79 | classNames.put(className, getNewClassName()); | 79 | private Multiset<String> constructors; |
| 80 | } | 80 | private String staticInitializer; |
| 81 | return classNames.get(className); | 81 | private String extendz; |
| 82 | } | 82 | private Multiset<String> implementz; |
| 83 | 83 | private Set<String> stringLiterals; | |
| 84 | private String getNewClassName() { | 84 | private Multiset<String> implementations; |
| 85 | return String.format("C%03d", classNames.size()); | 85 | private Multiset<String> references; |
| 86 | } | 86 | private String outer; |
| 87 | }; | 87 | |
| 88 | 88 | public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { | |
| 89 | public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { | 89 | this.namer = namer; |
| 90 | this.namer = namer; | 90 | |
| 91 | 91 | // stuff from the bytecode | |
| 92 | // stuff from the bytecode | 92 | |
| 93 | 93 | this.classEntry = EntryFactory.getClassEntry(c); | |
| 94 | this.classEntry = EntryFactory.getClassEntry(c); | 94 | this.fields = HashMultiset.create(); |
| 95 | this.fields = HashMultiset.create(); | 95 | for (CtField field : c.getDeclaredFields()) { |
| 96 | for (CtField field : c.getDeclaredFields()) { | 96 | this.fields.add(scrubType(field.getSignature())); |
| 97 | this.fields.add(scrubType(field.getSignature())); | 97 | } |
| 98 | } | 98 | this.methods = HashMultiset.create(); |
| 99 | this.methods = HashMultiset.create(); | 99 | for (CtMethod method : c.getDeclaredMethods()) { |
| 100 | for (CtMethod method : c.getDeclaredMethods()) { | 100 | this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); |
| 101 | this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); | 101 | } |
| 102 | } | 102 | this.constructors = HashMultiset.create(); |
| 103 | this.constructors = HashMultiset.create(); | 103 | for (CtConstructor constructor : c.getDeclaredConstructors()) { |
| 104 | for (CtConstructor constructor : c.getDeclaredConstructors()) { | 104 | this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); |
| 105 | this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); | 105 | } |
| 106 | } | 106 | this.staticInitializer = ""; |
| 107 | this.staticInitializer = ""; | 107 | if (c.getClassInitializer() != null) { |
| 108 | if (c.getClassInitializer() != null) { | 108 | this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); |
| 109 | this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); | 109 | } |
| 110 | } | 110 | this.extendz = ""; |
| 111 | this.extendz = ""; | 111 | if (c.getClassFile().getSuperclass() != null) { |
| 112 | if (c.getClassFile().getSuperclass() != null) { | 112 | this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); |
| 113 | this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); | 113 | } |
| 114 | } | 114 | this.implementz = HashMultiset.create(); |
| 115 | this.implementz = HashMultiset.create(); | 115 | for (String interfaceName : c.getClassFile().getInterfaces()) { |
| 116 | for (String interfaceName : c.getClassFile().getInterfaces()) { | 116 | this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); |
| 117 | this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); | 117 | } |
| 118 | } | 118 | |
| 119 | 119 | this.stringLiterals = Sets.newHashSet(); | |
| 120 | this.stringLiterals = Sets.newHashSet(); | 120 | ConstPool constants = c.getClassFile().getConstPool(); |
| 121 | ConstPool constants = c.getClassFile().getConstPool(); | 121 | for (int i = 1; i < constants.getSize(); i++) { |
| 122 | for (int i = 1; i < constants.getSize(); i++) { | 122 | if (constants.getTag(i) == ConstPool.CONST_String) { |
| 123 | if (constants.getTag(i) == ConstPool.CONST_String) { | 123 | this.stringLiterals.add(constants.getStringInfo(i)); |
| 124 | this.stringLiterals.add(constants.getStringInfo(i)); | 124 | } |
| 125 | } | 125 | } |
| 126 | } | 126 | |
| 127 | 127 | // stuff from the jar index | |
| 128 | // stuff from the jar index | 128 | |
| 129 | 129 | this.implementations = HashMultiset.create(); | |
| 130 | this.implementations = HashMultiset.create(); | 130 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); |
| 131 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); | 131 | if (implementationsNode != null) { |
| 132 | if (implementationsNode != null) { | 132 | @SuppressWarnings("unchecked") |
| 133 | @SuppressWarnings("unchecked") | 133 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); |
| 134 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); | 134 | while (implementations.hasMoreElements()) { |
| 135 | while (implementations.hasMoreElements()) { | 135 | ClassImplementationsTreeNode node = implementations.nextElement(); |
| 136 | ClassImplementationsTreeNode node = implementations.nextElement(); | 136 | this.implementations.add(scrubClassName(node.getClassEntry().getName())); |
| 137 | this.implementations.add(scrubClassName(node.getClassEntry().getName())); | 137 | } |
| 138 | } | 138 | } |
| 139 | } | 139 | |
| 140 | 140 | this.references = HashMultiset.create(); | |
| 141 | this.references = HashMultiset.create(); | 141 | if (useReferences) { |
| 142 | if (useReferences) { | 142 | for (CtField field : c.getDeclaredFields()) { |
| 143 | for (CtField field : c.getDeclaredFields()) { | 143 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); |
| 144 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); | 144 | index.getFieldReferences(fieldEntry).forEach(this::addReference); |
| 145 | index.getFieldReferences(fieldEntry).forEach(this::addReference); | 145 | } |
| 146 | } | 146 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 147 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 147 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 148 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 148 | index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); |
| 149 | index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); | 149 | } |
| 150 | } | 150 | } |
| 151 | } | 151 | |
| 152 | 152 | this.outer = null; | |
| 153 | this.outer = null; | 153 | if (this.classEntry.isInnerClass()) { |
| 154 | if (this.classEntry.isInnerClass()) { | 154 | this.outer = this.classEntry.getOuterClassName(); |
| 155 | this.outer = this.classEntry.getOuterClassName(); | 155 | } |
| 156 | } | 156 | } |
| 157 | } | 157 | |
| 158 | 158 | private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) { | |
| 159 | private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) { | 159 | if (reference.context.getSignature() != null) { |
| 160 | if (reference.context.getSignature() != null) { | 160 | this.references.add(String.format("%s_%s", |
| 161 | this.references.add(String.format("%s_%s", | 161 | scrubClassName(reference.context.getClassName()), |
| 162 | scrubClassName(reference.context.getClassName()), | 162 | scrubSignature(reference.context.getSignature()) |
| 163 | scrubSignature(reference.context.getSignature()) | 163 | )); |
| 164 | )); | 164 | } else { |
| 165 | } else { | 165 | this.references.add(String.format("%s_<clinit>", |
| 166 | this.references.add(String.format("%s_<clinit>", | 166 | scrubClassName(reference.context.getClassName()) |
| 167 | scrubClassName(reference.context.getClassName()) | 167 | )); |
| 168 | )); | 168 | } |
| 169 | } | 169 | } |
| 170 | } | 170 | |
| 171 | 171 | public ClassEntry getClassEntry() { | |
| 172 | public ClassEntry getClassEntry() { | 172 | return this.classEntry; |
| 173 | return this.classEntry; | 173 | } |
| 174 | } | 174 | |
| 175 | 175 | @Override | |
| 176 | @Override | 176 | public String toString() { |
| 177 | public String toString() { | 177 | StringBuilder buf = new StringBuilder(); |
| 178 | StringBuilder buf = new StringBuilder(); | 178 | buf.append("class: "); |
| 179 | buf.append("class: "); | 179 | buf.append(this.classEntry.getName()); |
| 180 | buf.append(this.classEntry.getName()); | 180 | buf.append(" "); |
| 181 | buf.append(" "); | 181 | buf.append(hashCode()); |
| 182 | buf.append(hashCode()); | 182 | buf.append("\n"); |
| 183 | buf.append("\n"); | 183 | for (String field : this.fields) { |
| 184 | for (String field : this.fields) { | 184 | buf.append("\tfield "); |
| 185 | buf.append("\tfield "); | 185 | buf.append(field); |
| 186 | buf.append(field); | 186 | buf.append("\n"); |
| 187 | buf.append("\n"); | 187 | } |
| 188 | } | 188 | for (String method : this.methods) { |
| 189 | for (String method : this.methods) { | 189 | buf.append("\tmethod "); |
| 190 | buf.append("\tmethod "); | 190 | buf.append(method); |
| 191 | buf.append(method); | 191 | buf.append("\n"); |
| 192 | buf.append("\n"); | 192 | } |
| 193 | } | 193 | for (String constructor : this.constructors) { |
| 194 | for (String constructor : this.constructors) { | 194 | buf.append("\tconstructor "); |
| 195 | buf.append("\tconstructor "); | 195 | buf.append(constructor); |
| 196 | buf.append(constructor); | 196 | buf.append("\n"); |
| 197 | buf.append("\n"); | 197 | } |
| 198 | } | 198 | if (!this.staticInitializer.isEmpty()) { |
| 199 | if (this.staticInitializer.length() > 0) { | 199 | buf.append("\tinitializer "); |
| 200 | buf.append("\tinitializer "); | 200 | buf.append(this.staticInitializer); |
| 201 | buf.append(this.staticInitializer); | 201 | buf.append("\n"); |
| 202 | buf.append("\n"); | 202 | } |
| 203 | } | 203 | if (!this.extendz.isEmpty()) { |
| 204 | if (this.extendz.length() > 0) { | 204 | buf.append("\textends "); |
| 205 | buf.append("\textends "); | 205 | buf.append(this.extendz); |
| 206 | buf.append(this.extendz); | 206 | buf.append("\n"); |
| 207 | buf.append("\n"); | 207 | } |
| 208 | } | 208 | for (String interfaceName : this.implementz) { |
| 209 | for (String interfaceName : this.implementz) { | 209 | buf.append("\timplements "); |
| 210 | buf.append("\timplements "); | 210 | buf.append(interfaceName); |
| 211 | buf.append(interfaceName); | 211 | buf.append("\n"); |
| 212 | buf.append("\n"); | 212 | } |
| 213 | } | 213 | for (String implementation : this.implementations) { |
| 214 | for (String implementation : this.implementations) { | 214 | buf.append("\timplemented by "); |
| 215 | buf.append("\timplemented by "); | 215 | buf.append(implementation); |
| 216 | buf.append(implementation); | 216 | buf.append("\n"); |
| 217 | buf.append("\n"); | 217 | } |
| 218 | } | 218 | for (String reference : this.references) { |
| 219 | for (String reference : this.references) { | 219 | buf.append("\treference "); |
| 220 | buf.append("\treference "); | 220 | buf.append(reference); |
| 221 | buf.append(reference); | 221 | buf.append("\n"); |
| 222 | buf.append("\n"); | 222 | } |
| 223 | } | 223 | buf.append("\touter "); |
| 224 | buf.append("\touter "); | 224 | buf.append(this.outer); |
| 225 | buf.append(this.outer); | 225 | buf.append("\n"); |
| 226 | buf.append("\n"); | 226 | return buf.toString(); |
| 227 | return buf.toString(); | 227 | } |
| 228 | } | 228 | |
| 229 | 229 | private String scrubClassName(String className) { | |
| 230 | private String scrubClassName(String className) { | 230 | return classNameReplacer.replace(className); |
| 231 | return classNameReplacer.replace(className); | 231 | } |
| 232 | } | 232 | |
| 233 | 233 | private String scrubType(String typeName) { | |
| 234 | private String scrubType(String typeName) { | 234 | return scrubType(new Type(typeName)).toString(); |
| 235 | return scrubType(new Type(typeName)).toString(); | 235 | } |
| 236 | } | 236 | |
| 237 | 237 | private Type scrubType(Type type) { | |
| 238 | private Type scrubType(Type type) { | 238 | if (type.hasClass()) { |
| 239 | if (type.hasClass()) { | 239 | return new Type(type, classNameReplacer); |
| 240 | return new Type(type, classNameReplacer); | 240 | } else { |
| 241 | } else { | 241 | return type; |
| 242 | return type; | 242 | } |
| 243 | } | 243 | } |
| 244 | } | 244 | |
| 245 | 245 | private String scrubSignature(String signature) { | |
| 246 | private String scrubSignature(String signature) { | 246 | return scrubSignature(new Signature(signature)).toString(); |
| 247 | return scrubSignature(new Signature(signature)).toString(); | 247 | } |
| 248 | } | 248 | |
| 249 | 249 | private Signature scrubSignature(Signature signature) { | |
| 250 | private Signature scrubSignature(Signature signature) { | 250 | return new Signature(signature, classNameReplacer); |
| 251 | return new Signature(signature, classNameReplacer); | 251 | } |
| 252 | } | 252 | |
| 253 | 253 | private boolean isClassMatchedUniquely(String className) { | |
| 254 | private boolean isClassMatchedUniquely(String className) { | 254 | return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; |
| 255 | return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; | 255 | } |
| 256 | } | 256 | |
| 257 | 257 | private String getBehaviorSignature(CtBehavior behavior) { | |
| 258 | private String getBehaviorSignature(CtBehavior behavior) { | 258 | try { |
| 259 | try { | 259 | // does this method have an implementation? |
| 260 | // does this method have an implementation? | 260 | if (behavior.getMethodInfo().getCodeAttribute() == null) { |
| 261 | if (behavior.getMethodInfo().getCodeAttribute() == null) { | 261 | return "(none)"; |
| 262 | return "(none)"; | 262 | } |
| 263 | } | 263 | |
| 264 | 264 | // compute the hash from the opcodes | |
| 265 | // compute the hash from the opcodes | 265 | ConstPool constants = behavior.getMethodInfo().getConstPool(); |
| 266 | ConstPool constants = behavior.getMethodInfo().getConstPool(); | 266 | final MessageDigest digest = MessageDigest.getInstance("MD5"); |
| 267 | final MessageDigest digest = MessageDigest.getInstance("MD5"); | 267 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); |
| 268 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); | 268 | while (iter.hasNext()) { |
| 269 | while (iter.hasNext()) { | 269 | int pos = iter.next(); |
| 270 | int pos = iter.next(); | 270 | |
| 271 | 271 | // update the hash with the opcode | |
| 272 | // update the hash with the opcode | 272 | int opcode = iter.byteAt(pos); |
| 273 | int opcode = iter.byteAt(pos); | 273 | digest.update((byte) opcode); |
| 274 | digest.update((byte) opcode); | 274 | int constIndex; |
| 275 | int constIndex; | 275 | switch (opcode) { |
| 276 | switch (opcode) { | 276 | case Opcode.LDC: |
| 277 | case Opcode.LDC: | 277 | constIndex = iter.byteAt(pos + 1); |
| 278 | constIndex = iter.byteAt(pos + 1); | 278 | updateHashWithConstant(digest, constants, constIndex); |
| 279 | updateHashWithConstant(digest, constants, constIndex); | 279 | break; |
| 280 | break; | 280 | |
| 281 | 281 | case Opcode.LDC_W: | |
| 282 | case Opcode.LDC_W: | 282 | case Opcode.LDC2_W: |
| 283 | case Opcode.LDC2_W: | 283 | constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); |
| 284 | constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); | 284 | updateHashWithConstant(digest, constants, constIndex); |
| 285 | updateHashWithConstant(digest, constants, constIndex); | 285 | break; |
| 286 | break; | 286 | default: |
| 287 | default: | 287 | break; |
| 288 | break; | 288 | } |
| 289 | } | 289 | } |
| 290 | } | 290 | |
| 291 | 291 | // update hash with method and field accesses | |
| 292 | // update hash with method and field accesses | 292 | behavior.instrument(new ExprEditor() { |
| 293 | behavior.instrument(new ExprEditor() { | 293 | @Override |
| 294 | @Override | 294 | public void edit(MethodCall call) { |
| 295 | public void edit(MethodCall call) { | 295 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); |
| 296 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); | 296 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 297 | updateHashWithString(digest, scrubSignature(call.getSignature())); | 297 | if (isClassMatchedUniquely(call.getClassName())) { |
| 298 | if (isClassMatchedUniquely(call.getClassName())) { | 298 | updateHashWithString(digest, call.getMethodName()); |
| 299 | updateHashWithString(digest, call.getMethodName()); | 299 | } |
| 300 | } | 300 | } |
| 301 | } | 301 | |
| 302 | 302 | @Override | |
| 303 | @Override | 303 | public void edit(FieldAccess access) { |
| 304 | public void edit(FieldAccess access) { | 304 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); |
| 305 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); | 305 | updateHashWithString(digest, scrubType(access.getSignature())); |
| 306 | updateHashWithString(digest, scrubType(access.getSignature())); | 306 | if (isClassMatchedUniquely(access.getClassName())) { |
| 307 | if (isClassMatchedUniquely(access.getClassName())) { | 307 | updateHashWithString(digest, access.getFieldName()); |
| 308 | updateHashWithString(digest, access.getFieldName()); | 308 | } |
| 309 | } | 309 | } |
| 310 | } | 310 | |
| 311 | 311 | @Override | |
| 312 | @Override | 312 | public void edit(ConstructorCall call) { |
| 313 | public void edit(ConstructorCall call) { | 313 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); |
| 314 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); | 314 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 315 | updateHashWithString(digest, scrubSignature(call.getSignature())); | 315 | } |
| 316 | } | 316 | |
| 317 | 317 | @Override | |
| 318 | @Override | 318 | public void edit(NewExpr expr) { |
| 319 | public void edit(NewExpr expr) { | 319 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); |
| 320 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); | 320 | } |
| 321 | } | 321 | }); |
| 322 | }); | 322 | |
| 323 | 323 | // convert the hash to a hex string | |
| 324 | // convert the hash to a hex string | 324 | return toHex(digest.digest()); |
| 325 | return toHex(digest.digest()); | 325 | } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { |
| 326 | } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { | 326 | throw new Error(ex); |
| 327 | throw new Error(ex); | 327 | } |
| 328 | } | 328 | } |
| 329 | } | 329 | |
| 330 | 330 | private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { | |
| 331 | private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { | 331 | ConstPoolEditor editor = new ConstPoolEditor(constants); |
| 332 | ConstPoolEditor editor = new ConstPoolEditor(constants); | 332 | ConstInfoAccessor item = editor.getItem(index); |
| 333 | ConstInfoAccessor item = editor.getItem(index); | 333 | if (item.getType() == InfoType.StringInfo) { |
| 334 | if (item.getType() == InfoType.StringInfo) { | 334 | updateHashWithString(digest, constants.getStringInfo(index)); |
| 335 | updateHashWithString(digest, constants.getStringInfo(index)); | 335 | } |
| 336 | } | 336 | // TODO: other constants |
| 337 | // TODO: other constants | 337 | } |
| 338 | } | 338 | |
| 339 | 339 | private void updateHashWithString(MessageDigest digest, String val) { | |
| 340 | private void updateHashWithString(MessageDigest digest, String val) { | 340 | try { |
| 341 | try { | 341 | digest.update(val.getBytes("UTF8")); |
| 342 | digest.update(val.getBytes("UTF8")); | 342 | } catch (UnsupportedEncodingException ex) { |
| 343 | } catch (UnsupportedEncodingException ex) { | 343 | throw new Error(ex); |
| 344 | throw new Error(ex); | 344 | } |
| 345 | } | 345 | } |
| 346 | } | 346 | |
| 347 | 347 | private String toHex(byte[] bytes) { | |
| 348 | private String toHex(byte[] bytes) { | 348 | // function taken from: |
| 349 | // function taken from: | 349 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java |
| 350 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java | 350 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); |
| 351 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); | 351 | char[] hexChars = new char[bytes.length * 2]; |
| 352 | char[] hexChars = new char[bytes.length * 2]; | 352 | for (int j = 0; j < bytes.length; j++) { |
| 353 | for (int j = 0; j < bytes.length; j++) { | 353 | int v = bytes[j] & 0xFF; |
| 354 | int v = bytes[j] & 0xFF; | 354 | hexChars[j * 2] = hexArray[v >>> 4]; |
| 355 | hexChars[j * 2] = hexArray[v >>> 4]; | 355 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; |
| 356 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | 356 | } |
| 357 | } | 357 | return new String(hexChars); |
| 358 | return new String(hexChars); | 358 | } |
| 359 | } | 359 | |
| 360 | 360 | @Override | |
| 361 | @Override | 361 | public boolean equals(Object other) { |
| 362 | public boolean equals(Object other) { | 362 | return other instanceof ClassIdentity && equals((ClassIdentity) other); |
| 363 | return other instanceof ClassIdentity && equals((ClassIdentity) other); | 363 | } |
| 364 | } | 364 | |
| 365 | 365 | public boolean equals(ClassIdentity other) { | |
| 366 | public boolean equals(ClassIdentity other) { | 366 | return this.fields.equals(other.fields) |
| 367 | return this.fields.equals(other.fields) | 367 | && this.methods.equals(other.methods) |
| 368 | && this.methods.equals(other.methods) | 368 | && this.constructors.equals(other.constructors) |
| 369 | && this.constructors.equals(other.constructors) | 369 | && this.staticInitializer.equals(other.staticInitializer) |
| 370 | && this.staticInitializer.equals(other.staticInitializer) | 370 | && this.extendz.equals(other.extendz) |
| 371 | && this.extendz.equals(other.extendz) | 371 | && this.implementz.equals(other.implementz) |
| 372 | && this.implementz.equals(other.implementz) | 372 | && this.implementations.equals(other.implementations) |
| 373 | && this.implementations.equals(other.implementations) | 373 | && this.references.equals(other.references); |
| 374 | && this.references.equals(other.references); | 374 | } |
| 375 | } | 375 | |
| 376 | 376 | @Override | |
| 377 | @Override | 377 | public int hashCode() { |
| 378 | public int hashCode() { | 378 | List<Object> objs = Lists.newArrayList(); |
| 379 | List<Object> objs = Lists.newArrayList(); | 379 | objs.addAll(this.fields); |
| 380 | objs.addAll(this.fields); | 380 | objs.addAll(this.methods); |
| 381 | objs.addAll(this.methods); | 381 | objs.addAll(this.constructors); |
| 382 | objs.addAll(this.constructors); | 382 | objs.add(this.staticInitializer); |
| 383 | objs.add(this.staticInitializer); | 383 | objs.add(this.extendz); |
| 384 | objs.add(this.extendz); | 384 | objs.addAll(this.implementz); |
| 385 | objs.addAll(this.implementz); | 385 | objs.addAll(this.implementations); |
| 386 | objs.addAll(this.implementations); | 386 | objs.addAll(this.references); |
| 387 | objs.addAll(this.references); | 387 | return Utils.combineHashesOrdered(objs); |
| 388 | return Utils.combineHashesOrdered(objs); | 388 | } |
| 389 | } | 389 | |
| 390 | 390 | public int getMatchScore(ClassIdentity other) { | |
| 391 | public int getMatchScore(ClassIdentity other) { | 391 | return 2 * getNumMatches(this.extendz, other.extendz) |
| 392 | return 2 * getNumMatches(this.extendz, other.extendz) | 392 | + 2 * getNumMatches(this.outer, other.outer) |
| 393 | + 2 * getNumMatches(this.outer, other.outer) | 393 | + 2 * getNumMatches(this.implementz, other.implementz) |
| 394 | + 2 * getNumMatches(this.implementz, other.implementz) | 394 | + getNumMatches(this.stringLiterals, other.stringLiterals) |
| 395 | + getNumMatches(this.stringLiterals, other.stringLiterals) | 395 | + getNumMatches(this.fields, other.fields) |
| 396 | + getNumMatches(this.fields, other.fields) | 396 | + getNumMatches(this.methods, other.methods) |
| 397 | + getNumMatches(this.methods, other.methods) | 397 | + getNumMatches(this.constructors, other.constructors); |
| 398 | + getNumMatches(this.constructors, other.constructors); | 398 | } |
| 399 | } | 399 | |
| 400 | 400 | public int getMaxMatchScore() { | |
| 401 | public int getMaxMatchScore() { | 401 | return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); |
| 402 | return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); | 402 | } |
| 403 | } | 403 | |
| 404 | 404 | public boolean matches(CtClass c) { | |
| 405 | public boolean matches(CtClass c) { | 405 | // just compare declaration counts |
| 406 | // just compare declaration counts | 406 | return this.fields.size() == c.getDeclaredFields().length |
| 407 | return this.fields.size() == c.getDeclaredFields().length | 407 | && this.methods.size() == c.getDeclaredMethods().length |
| 408 | && this.methods.size() == c.getDeclaredMethods().length | 408 | && this.constructors.size() == c.getDeclaredConstructors().length; |
| 409 | && this.constructors.size() == c.getDeclaredConstructors().length; | 409 | } |
| 410 | } | 410 | |
| 411 | 411 | private int getNumMatches(Set<String> a, Set<String> b) { | |
| 412 | private int getNumMatches(Set<String> a, Set<String> b) { | 412 | int numMatches = 0; |
| 413 | int numMatches = 0; | 413 | for (String val : a) { |
| 414 | for (String val : a) { | 414 | if (b.contains(val)) { |
| 415 | if (b.contains(val)) { | 415 | numMatches++; |
| 416 | numMatches++; | 416 | } |
| 417 | } | 417 | } |
| 418 | } | 418 | return numMatches; |
| 419 | return numMatches; | 419 | } |
| 420 | } | 420 | |
| 421 | 421 | private int getNumMatches(Multiset<String> a, Multiset<String> b) { | |
| 422 | private int getNumMatches(Multiset<String> a, Multiset<String> b) { | 422 | int numMatches = 0; |
| 423 | int numMatches = 0; | 423 | for (String val : a) { |
| 424 | for (String val : a) { | 424 | if (b.contains(val)) { |
| 425 | if (b.contains(val)) { | 425 | numMatches++; |
| 426 | numMatches++; | 426 | } |
| 427 | } | 427 | } |
| 428 | } | 428 | return numMatches; |
| 429 | return numMatches; | 429 | } |
| 430 | } | 430 | |
| 431 | 431 | private int getNumMatches(String a, String b) { | |
| 432 | private int getNumMatches(String a, String b) { | 432 | if (a == null && b == null) { |
| 433 | if (a == null && b == null) { | 433 | return 1; |
| 434 | return 1; | 434 | } else if (a != null && b != null && a.equals(b)) { |
| 435 | } else if (a != null && b != null && a.equals(b)) { | 435 | return 1; |
| 436 | return 1; | 436 | } |
| 437 | } | 437 | return 0; |
| 438 | return 0; | 438 | } |
| 439 | } | ||
| 440 | } | 439 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatch.java b/src/main/java/cuchaz/enigma/convert/ClassMatch.java index 9fa35f03..bb3e4f43 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatch.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatch.java | |||
| @@ -8,76 +8,76 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 14 | 17 | ||
| 15 | import java.util.Collection; | 18 | import java.util.Collection; |
| 16 | import java.util.Set; | 19 | import java.util.Set; |
| 17 | 20 | ||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 19 | import cuchaz.enigma.utils.Utils; | ||
| 20 | |||
| 21 | public class ClassMatch { | 21 | public class ClassMatch { |
| 22 | 22 | ||
| 23 | public Set<ClassEntry> sourceClasses; | 23 | public Set<ClassEntry> sourceClasses; |
| 24 | public Set<ClassEntry> destClasses; | 24 | public Set<ClassEntry> destClasses; |
| 25 | 25 | ||
| 26 | public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) { | 26 | public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) { |
| 27 | this.sourceClasses = Sets.newHashSet(sourceClasses); | 27 | this.sourceClasses = Sets.newHashSet(sourceClasses); |
| 28 | this.destClasses = Sets.newHashSet(destClasses); | 28 | this.destClasses = Sets.newHashSet(destClasses); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { | 31 | public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { |
| 32 | sourceClasses = Sets.newHashSet(); | 32 | sourceClasses = Sets.newHashSet(); |
| 33 | if (sourceClass != null) { | 33 | if (sourceClass != null) { |
| 34 | sourceClasses.add(sourceClass); | 34 | sourceClasses.add(sourceClass); |
| 35 | } | 35 | } |
| 36 | destClasses = Sets.newHashSet(); | 36 | destClasses = Sets.newHashSet(); |
| 37 | if (destClass != null) { | 37 | if (destClass != null) { |
| 38 | destClasses.add(destClass); | 38 | destClasses.add(destClass); |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | public boolean isMatched() { | 42 | public boolean isMatched() { |
| 43 | return sourceClasses.size() > 0 && destClasses.size() > 0; | 43 | return !sourceClasses.isEmpty() && !destClasses.isEmpty(); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | public boolean isAmbiguous() { | 46 | public boolean isAmbiguous() { |
| 47 | return sourceClasses.size() > 1 || destClasses.size() > 1; | 47 | return sourceClasses.size() > 1 || destClasses.size() > 1; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public ClassEntry getUniqueSource() { | 50 | public ClassEntry getUniqueSource() { |
| 51 | if (sourceClasses.size() != 1) { | 51 | if (sourceClasses.size() != 1) { |
| 52 | throw new IllegalStateException("Match has ambiguous source!"); | 52 | throw new IllegalStateException("Match has ambiguous source!"); |
| 53 | } | 53 | } |
| 54 | return sourceClasses.iterator().next(); | 54 | return sourceClasses.iterator().next(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | public ClassEntry getUniqueDest() { | 57 | public ClassEntry getUniqueDest() { |
| 58 | if (destClasses.size() != 1) { | 58 | if (destClasses.size() != 1) { |
| 59 | throw new IllegalStateException("Match has ambiguous source!"); | 59 | throw new IllegalStateException("Match has ambiguous source!"); |
| 60 | } | 60 | } |
| 61 | return destClasses.iterator().next(); | 61 | return destClasses.iterator().next(); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) { | 64 | public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) { |
| 65 | Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses); | 65 | Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses); |
| 66 | intersection.retainAll(classes); | 66 | intersection.retainAll(classes); |
| 67 | return intersection; | 67 | return intersection; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | @Override | 70 | @Override |
| 71 | public int hashCode() { | 71 | public int hashCode() { |
| 72 | return Utils.combineHashesOrdered(sourceClasses, destClasses); | 72 | return Utils.combineHashesOrdered(sourceClasses, destClasses); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | @Override | 75 | @Override |
| 76 | public boolean equals(Object other) { | 76 | public boolean equals(Object other) { |
| 77 | return other instanceof ClassMatch && equals((ClassMatch) other); | 77 | return other instanceof ClassMatch && equals((ClassMatch) other); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | public boolean equals(ClassMatch other) { | 80 | public boolean equals(ClassMatch other) { |
| 81 | return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); | 81 | return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); |
| 82 | } | 82 | } |
| 83 | } | 83 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatches.java b/src/main/java/cuchaz/enigma/convert/ClassMatches.java index 431c4f24..db2c550f 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatches.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatches.java | |||
| @@ -8,152 +8,151 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.HashBiMap; | 15 | import com.google.common.collect.HashBiMap; |
| 15 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Sets; | 17 | import com.google.common.collect.Sets; |
| 17 | |||
| 18 | import java.util.*; | ||
| 19 | |||
| 20 | import cuchaz.enigma.mapping.ClassEntry; | 18 | import cuchaz.enigma.mapping.ClassEntry; |
| 21 | 19 | ||
| 20 | import java.util.*; | ||
| 22 | 21 | ||
| 23 | public class ClassMatches implements Iterable<ClassMatch> { | 22 | public class ClassMatches implements Iterable<ClassMatch> { |
| 24 | 23 | ||
| 25 | private Collection<ClassMatch> matches; | 24 | private Collection<ClassMatch> matches; |
| 26 | private Map<ClassEntry, ClassMatch> matchesBySource; | 25 | private Map<ClassEntry, ClassMatch> matchesBySource; |
| 27 | private Map<ClassEntry, ClassMatch> matchesByDest; | 26 | private Map<ClassEntry, ClassMatch> matchesByDest; |
| 28 | private BiMap<ClassEntry, ClassEntry> uniqueMatches; | 27 | private BiMap<ClassEntry, ClassEntry> uniqueMatches; |
| 29 | private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource; | 28 | private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource; |
| 30 | private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest; | 29 | private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest; |
| 31 | private Set<ClassEntry> unmatchedSourceClasses; | 30 | private Set<ClassEntry> unmatchedSourceClasses; |
| 32 | private Set<ClassEntry> unmatchedDestClasses; | 31 | private Set<ClassEntry> unmatchedDestClasses; |
| 33 | 32 | ||
| 34 | public ClassMatches() { | 33 | public ClassMatches() { |
| 35 | this(new ArrayList<>()); | 34 | this(new ArrayList<>()); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | public ClassMatches(Collection<ClassMatch> matches) { | 37 | public ClassMatches(Collection<ClassMatch> matches) { |
| 39 | this.matches = matches; | 38 | this.matches = matches; |
| 40 | matchesBySource = Maps.newHashMap(); | 39 | matchesBySource = Maps.newHashMap(); |
| 41 | matchesByDest = Maps.newHashMap(); | 40 | matchesByDest = Maps.newHashMap(); |
| 42 | uniqueMatches = HashBiMap.create(); | 41 | uniqueMatches = HashBiMap.create(); |
| 43 | ambiguousMatchesBySource = Maps.newHashMap(); | 42 | ambiguousMatchesBySource = Maps.newHashMap(); |
| 44 | ambiguousMatchesByDest = Maps.newHashMap(); | 43 | ambiguousMatchesByDest = Maps.newHashMap(); |
| 45 | unmatchedSourceClasses = Sets.newHashSet(); | 44 | unmatchedSourceClasses = Sets.newHashSet(); |
| 46 | unmatchedDestClasses = Sets.newHashSet(); | 45 | unmatchedDestClasses = Sets.newHashSet(); |
| 47 | 46 | ||
| 48 | for (ClassMatch match : matches) { | 47 | for (ClassMatch match : matches) { |
| 49 | indexMatch(match); | 48 | indexMatch(match); |
| 50 | } | 49 | } |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public void add(ClassMatch match) { | 52 | public void add(ClassMatch match) { |
| 54 | matches.add(match); | 53 | matches.add(match); |
| 55 | indexMatch(match); | 54 | indexMatch(match); |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | public void remove(ClassMatch match) { | 57 | public void remove(ClassMatch match) { |
| 59 | for (ClassEntry sourceClass : match.sourceClasses) { | 58 | for (ClassEntry sourceClass : match.sourceClasses) { |
| 60 | matchesBySource.remove(sourceClass); | 59 | matchesBySource.remove(sourceClass); |
| 61 | uniqueMatches.remove(sourceClass); | 60 | uniqueMatches.remove(sourceClass); |
| 62 | ambiguousMatchesBySource.remove(sourceClass); | 61 | ambiguousMatchesBySource.remove(sourceClass); |
| 63 | unmatchedSourceClasses.remove(sourceClass); | 62 | unmatchedSourceClasses.remove(sourceClass); |
| 64 | } | 63 | } |
| 65 | for (ClassEntry destClass : match.destClasses) { | 64 | for (ClassEntry destClass : match.destClasses) { |
| 66 | matchesByDest.remove(destClass); | 65 | matchesByDest.remove(destClass); |
| 67 | uniqueMatches.inverse().remove(destClass); | 66 | uniqueMatches.inverse().remove(destClass); |
| 68 | ambiguousMatchesByDest.remove(destClass); | 67 | ambiguousMatchesByDest.remove(destClass); |
| 69 | unmatchedDestClasses.remove(destClass); | 68 | unmatchedDestClasses.remove(destClass); |
| 70 | } | 69 | } |
| 71 | matches.remove(match); | 70 | matches.remove(match); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | public int size() { | 73 | public int size() { |
| 75 | return matches.size(); | 74 | return matches.size(); |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | @Override | 77 | @Override |
| 79 | public Iterator<ClassMatch> iterator() { | 78 | public Iterator<ClassMatch> iterator() { |
| 80 | return matches.iterator(); | 79 | return matches.iterator(); |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | private void indexMatch(ClassMatch match) { | 82 | private void indexMatch(ClassMatch match) { |
| 84 | if (!match.isMatched()) { | 83 | if (!match.isMatched()) { |
| 85 | // unmatched | 84 | // unmatched |
| 86 | unmatchedSourceClasses.addAll(match.sourceClasses); | 85 | unmatchedSourceClasses.addAll(match.sourceClasses); |
| 87 | unmatchedDestClasses.addAll(match.destClasses); | 86 | unmatchedDestClasses.addAll(match.destClasses); |
| 88 | } else { | 87 | } else { |
| 89 | if (match.isAmbiguous()) { | 88 | if (match.isAmbiguous()) { |
| 90 | // ambiguously matched | 89 | // ambiguously matched |
| 91 | for (ClassEntry entry : match.sourceClasses) { | 90 | for (ClassEntry entry : match.sourceClasses) { |
| 92 | ambiguousMatchesBySource.put(entry, match); | 91 | ambiguousMatchesBySource.put(entry, match); |
| 93 | } | 92 | } |
| 94 | for (ClassEntry entry : match.destClasses) { | 93 | for (ClassEntry entry : match.destClasses) { |
| 95 | ambiguousMatchesByDest.put(entry, match); | 94 | ambiguousMatchesByDest.put(entry, match); |
| 96 | } | 95 | } |
| 97 | } else { | 96 | } else { |
| 98 | // uniquely matched | 97 | // uniquely matched |
| 99 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); | 98 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); |
| 100 | } | 99 | } |
| 101 | } | 100 | } |
| 102 | for (ClassEntry entry : match.sourceClasses) { | 101 | for (ClassEntry entry : match.sourceClasses) { |
| 103 | matchesBySource.put(entry, match); | 102 | matchesBySource.put(entry, match); |
| 104 | } | 103 | } |
| 105 | for (ClassEntry entry : match.destClasses) { | 104 | for (ClassEntry entry : match.destClasses) { |
| 106 | matchesByDest.put(entry, match); | 105 | matchesByDest.put(entry, match); |
| 107 | } | 106 | } |
| 108 | } | 107 | } |
| 109 | 108 | ||
| 110 | public BiMap<ClassEntry, ClassEntry> getUniqueMatches() { | 109 | public BiMap<ClassEntry, ClassEntry> getUniqueMatches() { |
| 111 | return uniqueMatches; | 110 | return uniqueMatches; |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | public Set<ClassEntry> getUnmatchedSourceClasses() { | 113 | public Set<ClassEntry> getUnmatchedSourceClasses() { |
| 115 | return unmatchedSourceClasses; | 114 | return unmatchedSourceClasses; |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | public Set<ClassEntry> getUnmatchedDestClasses() { | 117 | public Set<ClassEntry> getUnmatchedDestClasses() { |
| 119 | return unmatchedDestClasses; | 118 | return unmatchedDestClasses; |
| 120 | } | 119 | } |
| 121 | 120 | ||
| 122 | public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { | 121 | public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { |
| 123 | return ambiguousMatchesBySource.keySet(); | 122 | return ambiguousMatchesBySource.keySet(); |
| 124 | } | 123 | } |
| 125 | 124 | ||
| 126 | public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { | 125 | public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { |
| 127 | return ambiguousMatchesBySource.get(sourceClass); | 126 | return ambiguousMatchesBySource.get(sourceClass); |
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | public ClassMatch getMatchBySource(ClassEntry sourceClass) { | 129 | public ClassMatch getMatchBySource(ClassEntry sourceClass) { |
| 131 | return matchesBySource.get(sourceClass); | 130 | return matchesBySource.get(sourceClass); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | public ClassMatch getMatchByDest(ClassEntry destClass) { | 133 | public ClassMatch getMatchByDest(ClassEntry destClass) { |
| 135 | return matchesByDest.get(destClass); | 134 | return matchesByDest.get(destClass); |
| 136 | } | 135 | } |
| 137 | 136 | ||
| 138 | public void removeSource(ClassEntry sourceClass) { | 137 | public void removeSource(ClassEntry sourceClass) { |
| 139 | ClassMatch match = matchesBySource.get(sourceClass); | 138 | ClassMatch match = matchesBySource.get(sourceClass); |
| 140 | if (match != null) { | 139 | if (match != null) { |
| 141 | remove(match); | 140 | remove(match); |
| 142 | match.sourceClasses.remove(sourceClass); | 141 | match.sourceClasses.remove(sourceClass); |
| 143 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { | 142 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { |
| 144 | add(match); | 143 | add(match); |
| 145 | } | 144 | } |
| 146 | } | 145 | } |
| 147 | } | 146 | } |
| 148 | 147 | ||
| 149 | public void removeDest(ClassEntry destClass) { | 148 | public void removeDest(ClassEntry destClass) { |
| 150 | ClassMatch match = matchesByDest.get(destClass); | 149 | ClassMatch match = matchesByDest.get(destClass); |
| 151 | if (match != null) { | 150 | if (match != null) { |
| 152 | remove(match); | 151 | remove(match); |
| 153 | match.destClasses.remove(destClass); | 152 | match.destClasses.remove(destClass); |
| 154 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { | 153 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { |
| 155 | add(match); | 154 | add(match); |
| 156 | } | 155 | } |
| 157 | } | 156 | } |
| 158 | } | 157 | } |
| 159 | } | 158 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatching.java b/src/main/java/cuchaz/enigma/convert/ClassMatching.java index b05df871..f302f130 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatching.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatching.java | |||
| @@ -8,12 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.HashBiMap; | 15 | import com.google.common.collect.HashBiMap; |
| 15 | import com.google.common.collect.Lists; | 16 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Sets; | 17 | import com.google.common.collect.Sets; |
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 17 | 19 | ||
| 18 | import java.util.ArrayList; | 20 | import java.util.ArrayList; |
| 19 | import java.util.Collection; | 21 | import java.util.Collection; |
| @@ -21,134 +23,132 @@ import java.util.List; | |||
| 21 | import java.util.Map.Entry; | 23 | import java.util.Map.Entry; |
| 22 | import java.util.Set; | 24 | import java.util.Set; |
| 23 | 25 | ||
| 24 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 25 | |||
| 26 | public class ClassMatching { | 26 | public class ClassMatching { |
| 27 | 27 | ||
| 28 | private ClassForest sourceClasses; | 28 | private ClassForest sourceClasses; |
| 29 | private ClassForest destClasses; | 29 | private ClassForest destClasses; |
| 30 | private BiMap<ClassEntry, ClassEntry> knownMatches; | 30 | private BiMap<ClassEntry, ClassEntry> knownMatches; |
| 31 | 31 | ||
| 32 | public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { | 32 | public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { |
| 33 | sourceClasses = new ClassForest(sourceIdentifier); | 33 | sourceClasses = new ClassForest(sourceIdentifier); |
| 34 | destClasses = new ClassForest(destIdentifier); | 34 | destClasses = new ClassForest(destIdentifier); |
| 35 | knownMatches = HashBiMap.create(); | 35 | knownMatches = HashBiMap.create(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) { | 38 | public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) { |
| 39 | this.knownMatches.putAll(knownMatches); | 39 | this.knownMatches.putAll(knownMatches); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) { | 42 | public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) { |
| 43 | for (ClassEntry sourceClass : sourceClasses) { | 43 | for (ClassEntry sourceClass : sourceClasses) { |
| 44 | if (!knownMatches.containsKey(sourceClass)) { | 44 | if (!knownMatches.containsKey(sourceClass)) { |
| 45 | this.sourceClasses.add(sourceClass); | 45 | this.sourceClasses.add(sourceClass); |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | for (ClassEntry destClass : destClasses) { | 48 | for (ClassEntry destClass : destClasses) { |
| 49 | if (!knownMatches.containsValue(destClass)) { | 49 | if (!knownMatches.containsValue(destClass)) { |
| 50 | this.destClasses.add(destClass); | 50 | this.destClasses.add(destClass); |
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | public Collection<ClassMatch> matches() { | 55 | public Collection<ClassMatch> matches() { |
| 56 | List<ClassMatch> matches = Lists.newArrayList(); | 56 | List<ClassMatch> matches = Lists.newArrayList(); |
| 57 | for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) { | 57 | for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) { |
| 58 | matches.add(new ClassMatch( | 58 | matches.add(new ClassMatch( |
| 59 | entry.getKey(), | 59 | entry.getKey(), |
| 60 | entry.getValue() | 60 | entry.getValue() |
| 61 | )); | 61 | )); |
| 62 | } | 62 | } |
| 63 | for (ClassIdentity identity : sourceClasses.identities()) { | 63 | for (ClassIdentity identity : sourceClasses.identities()) { |
| 64 | matches.add(new ClassMatch( | 64 | matches.add(new ClassMatch( |
| 65 | sourceClasses.getClasses(identity), | 65 | sourceClasses.getClasses(identity), |
| 66 | destClasses.getClasses(identity) | 66 | destClasses.getClasses(identity) |
| 67 | )); | 67 | )); |
| 68 | } | 68 | } |
| 69 | for (ClassIdentity identity : destClasses.identities()) { | 69 | for (ClassIdentity identity : destClasses.identities()) { |
| 70 | if (!sourceClasses.containsIdentity(identity)) { | 70 | if (!sourceClasses.containsIdentity(identity)) { |
| 71 | matches.add(new ClassMatch( | 71 | matches.add(new ClassMatch( |
| 72 | new ArrayList<>(), | 72 | new ArrayList<>(), |
| 73 | destClasses.getClasses(identity) | 73 | destClasses.getClasses(identity) |
| 74 | )); | 74 | )); |
| 75 | } | 75 | } |
| 76 | } | 76 | } |
| 77 | return matches; | 77 | return matches; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | public Collection<ClassEntry> sourceClasses() { | 80 | public Collection<ClassEntry> sourceClasses() { |
| 81 | Set<ClassEntry> classes = Sets.newHashSet(); | 81 | Set<ClassEntry> classes = Sets.newHashSet(); |
| 82 | for (ClassMatch match : matches()) { | 82 | for (ClassMatch match : matches()) { |
| 83 | classes.addAll(match.sourceClasses); | 83 | classes.addAll(match.sourceClasses); |
| 84 | } | 84 | } |
| 85 | return classes; | 85 | return classes; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | public Collection<ClassEntry> destClasses() { | 88 | public Collection<ClassEntry> destClasses() { |
| 89 | Set<ClassEntry> classes = Sets.newHashSet(); | 89 | Set<ClassEntry> classes = Sets.newHashSet(); |
| 90 | for (ClassMatch match : matches()) { | 90 | for (ClassMatch match : matches()) { |
| 91 | classes.addAll(match.destClasses); | 91 | classes.addAll(match.destClasses); |
| 92 | } | 92 | } |
| 93 | return classes; | 93 | return classes; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | public BiMap<ClassEntry, ClassEntry> uniqueMatches() { | 96 | public BiMap<ClassEntry, ClassEntry> uniqueMatches() { |
| 97 | BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create(); | 97 | BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create(); |
| 98 | for (ClassMatch match : matches()) { | 98 | for (ClassMatch match : matches()) { |
| 99 | if (match.isMatched() && !match.isAmbiguous()) { | 99 | if (match.isMatched() && !match.isAmbiguous()) { |
| 100 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); | 100 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); |
| 101 | } | 101 | } |
| 102 | } | 102 | } |
| 103 | return uniqueMatches; | 103 | return uniqueMatches; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | public Collection<ClassMatch> ambiguousMatches() { | 106 | public Collection<ClassMatch> ambiguousMatches() { |
| 107 | List<ClassMatch> ambiguousMatches = Lists.newArrayList(); | 107 | List<ClassMatch> ambiguousMatches = Lists.newArrayList(); |
| 108 | for (ClassMatch match : matches()) { | 108 | for (ClassMatch match : matches()) { |
| 109 | if (match.isMatched() && match.isAmbiguous()) { | 109 | if (match.isMatched() && match.isAmbiguous()) { |
| 110 | ambiguousMatches.add(match); | 110 | ambiguousMatches.add(match); |
| 111 | } | 111 | } |
| 112 | } | 112 | } |
| 113 | return ambiguousMatches; | 113 | return ambiguousMatches; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | public Collection<ClassEntry> unmatchedSourceClasses() { | 116 | public Collection<ClassEntry> unmatchedSourceClasses() { |
| 117 | List<ClassEntry> classes = Lists.newArrayList(); | 117 | List<ClassEntry> classes = Lists.newArrayList(); |
| 118 | for (ClassMatch match : matches()) { | 118 | for (ClassMatch match : matches()) { |
| 119 | if (!match.isMatched() && !match.sourceClasses.isEmpty()) { | 119 | if (!match.isMatched() && !match.sourceClasses.isEmpty()) { |
| 120 | classes.addAll(match.sourceClasses); | 120 | classes.addAll(match.sourceClasses); |
| 121 | } | 121 | } |
| 122 | } | 122 | } |
| 123 | return classes; | 123 | return classes; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | public Collection<ClassEntry> unmatchedDestClasses() { | 126 | public Collection<ClassEntry> unmatchedDestClasses() { |
| 127 | List<ClassEntry> classes = Lists.newArrayList(); | 127 | List<ClassEntry> classes = Lists.newArrayList(); |
| 128 | for (ClassMatch match : matches()) { | 128 | for (ClassMatch match : matches()) { |
| 129 | if (!match.isMatched() && !match.destClasses.isEmpty()) { | 129 | if (!match.isMatched() && !match.destClasses.isEmpty()) { |
| 130 | classes.addAll(match.destClasses); | 130 | classes.addAll(match.destClasses); |
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | return classes; | 133 | return classes; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | @Override | 136 | @Override |
| 137 | public String toString() { | 137 | public String toString() { |
| 138 | 138 | ||
| 139 | // count the ambiguous classes | 139 | // count the ambiguous classes |
| 140 | int numAmbiguousSource = 0; | 140 | int numAmbiguousSource = 0; |
| 141 | int numAmbiguousDest = 0; | 141 | int numAmbiguousDest = 0; |
| 142 | for (ClassMatch match : ambiguousMatches()) { | 142 | for (ClassMatch match : ambiguousMatches()) { |
| 143 | numAmbiguousSource += match.sourceClasses.size(); | 143 | numAmbiguousSource += match.sourceClasses.size(); |
| 144 | numAmbiguousDest += match.destClasses.size(); | 144 | numAmbiguousDest += match.destClasses.size(); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String | 147 | String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String |
| 148 | .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String | 148 | .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String |
| 149 | .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String | 149 | .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String |
| 150 | .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String | 150 | .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String |
| 151 | .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); | 151 | .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); |
| 152 | return buf; | 152 | return buf; |
| 153 | } | 153 | } |
| 154 | } | 154 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassNamer.java b/src/main/java/cuchaz/enigma/convert/ClassNamer.java index e471c7dd..39699101 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassNamer.java +++ b/src/main/java/cuchaz/enigma/convert/ClassNamer.java | |||
| @@ -8,49 +8,48 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 15 | 17 | ||
| 16 | import java.util.Map; | 18 | import java.util.Map; |
| 17 | 19 | ||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 19 | |||
| 20 | public class ClassNamer { | 20 | public class ClassNamer { |
| 21 | 21 | ||
| 22 | public interface SidedClassNamer { | 22 | private Map<String, String> sourceNames; |
| 23 | String getName(String name); | 23 | private Map<String, String> destNames; |
| 24 | } | 24 | public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) { |
| 25 | 25 | // convert the identity mappings to name maps | |
| 26 | private Map<String, String> sourceNames; | 26 | this.sourceNames = Maps.newHashMap(); |
| 27 | private Map<String, String> destNames; | 27 | this.destNames = Maps.newHashMap(); |
| 28 | 28 | int i = 0; | |
| 29 | public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) { | 29 | for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) { |
| 30 | // convert the identity mappings to name maps | 30 | String name = String.format("M%04d", i++); |
| 31 | this.sourceNames = Maps.newHashMap(); | 31 | this.sourceNames.put(entry.getKey().getName(), name); |
| 32 | this.destNames = Maps.newHashMap(); | 32 | this.destNames.put(entry.getValue().getName(), name); |
| 33 | int i = 0; | 33 | } |
| 34 | for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) { | 34 | } |
| 35 | String name = String.format("M%04d", i++); | 35 | |
| 36 | this.sourceNames.put(entry.getKey().getName(), name); | 36 | public String getSourceName(String name) { |
| 37 | this.destNames.put(entry.getValue().getName(), name); | 37 | return this.sourceNames.get(name); |
| 38 | } | 38 | } |
| 39 | } | 39 | |
| 40 | 40 | public String getDestName(String name) { | |
| 41 | public String getSourceName(String name) { | 41 | return this.destNames.get(name); |
| 42 | return this.sourceNames.get(name); | 42 | } |
| 43 | } | 43 | |
| 44 | 44 | public SidedClassNamer getSourceNamer() { | |
| 45 | public String getDestName(String name) { | 45 | return this::getSourceName; |
| 46 | return this.destNames.get(name); | 46 | } |
| 47 | } | 47 | |
| 48 | 48 | public SidedClassNamer getDestNamer() { | |
| 49 | public SidedClassNamer getSourceNamer() { | 49 | return this::getDestName; |
| 50 | return this::getSourceName; | 50 | } |
| 51 | } | 51 | |
| 52 | 52 | public interface SidedClassNamer { | |
| 53 | public SidedClassNamer getDestNamer() { | 53 | String getName(String name); |
| 54 | return this::getDestName; | 54 | } |
| 55 | } | ||
| 56 | } | 55 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/FieldMatches.java b/src/main/java/cuchaz/enigma/convert/FieldMatches.java index 236cd4d1..a528b276 100644 --- a/src/main/java/cuchaz/enigma/convert/FieldMatches.java +++ b/src/main/java/cuchaz/enigma/convert/FieldMatches.java | |||
| @@ -8,144 +8,143 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| 14 | |||
| 15 | import java.util.Collection; | ||
| 16 | import java.util.Set; | ||
| 17 | |||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.FieldEntry; | 16 | import cuchaz.enigma.mapping.FieldEntry; |
| 20 | 17 | ||
| 18 | import java.util.Collection; | ||
| 19 | import java.util.Set; | ||
| 21 | 20 | ||
| 22 | public class FieldMatches { | 21 | public class FieldMatches { |
| 23 | 22 | ||
| 24 | private BiMap<FieldEntry, FieldEntry> matches; | 23 | private BiMap<FieldEntry, FieldEntry> matches; |
| 25 | private Multimap<ClassEntry, FieldEntry> matchedSourceFields; | 24 | private Multimap<ClassEntry, FieldEntry> matchedSourceFields; |
| 26 | private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields; | 25 | private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields; |
| 27 | private Multimap<ClassEntry, FieldEntry> unmatchedDestFields; | 26 | private Multimap<ClassEntry, FieldEntry> unmatchedDestFields; |
| 28 | private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields; | 27 | private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields; |
| 29 | 28 | ||
| 30 | public FieldMatches() { | 29 | public FieldMatches() { |
| 31 | matches = HashBiMap.create(); | 30 | matches = HashBiMap.create(); |
| 32 | matchedSourceFields = HashMultimap.create(); | 31 | matchedSourceFields = HashMultimap.create(); |
| 33 | unmatchedSourceFields = HashMultimap.create(); | 32 | unmatchedSourceFields = HashMultimap.create(); |
| 34 | unmatchedDestFields = HashMultimap.create(); | 33 | unmatchedDestFields = HashMultimap.create(); |
| 35 | unmatchableSourceFields = HashMultimap.create(); | 34 | unmatchableSourceFields = HashMultimap.create(); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | public void addMatch(FieldEntry srcField, FieldEntry destField) { | 37 | public void addMatch(FieldEntry srcField, FieldEntry destField) { |
| 39 | boolean wasAdded = matches.put(srcField, destField) == null; | 38 | boolean wasAdded = matches.put(srcField, destField) == null; |
| 40 | assert (wasAdded); | 39 | assert (wasAdded); |
| 41 | wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); | 40 | wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); |
| 42 | assert (wasAdded); | 41 | assert (wasAdded); |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | public void addUnmatchedSourceField(FieldEntry fieldEntry) { | 44 | public void addUnmatchedSourceField(FieldEntry fieldEntry) { |
| 46 | boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); | 45 | boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); |
| 47 | assert (wasAdded); | 46 | assert (wasAdded); |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { | 49 | public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { |
| 51 | for (FieldEntry fieldEntry : fieldEntries) { | 50 | for (FieldEntry fieldEntry : fieldEntries) { |
| 52 | addUnmatchedSourceField(fieldEntry); | 51 | addUnmatchedSourceField(fieldEntry); |
| 53 | } | 52 | } |
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | public void addUnmatchedDestField(FieldEntry fieldEntry) { | 55 | public void addUnmatchedDestField(FieldEntry fieldEntry) { |
| 57 | boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); | 56 | boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); |
| 58 | assert (wasAdded); | 57 | assert (wasAdded); |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { | 60 | public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { |
| 62 | for (FieldEntry fieldEntry : fieldEntries) { | 61 | for (FieldEntry fieldEntry : fieldEntries) { |
| 63 | addUnmatchedDestField(fieldEntry); | 62 | addUnmatchedDestField(fieldEntry); |
| 64 | } | 63 | } |
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | public void addUnmatchableSourceField(FieldEntry sourceField) { | 66 | public void addUnmatchableSourceField(FieldEntry sourceField) { |
| 68 | boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); | 67 | boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); |
| 69 | assert (wasAdded); | 68 | assert (wasAdded); |
| 70 | } | 69 | } |
| 71 | 70 | ||
| 72 | public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { | 71 | public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { |
| 73 | return unmatchedSourceFields.keySet(); | 72 | return unmatchedSourceFields.keySet(); |
| 74 | } | 73 | } |
| 75 | 74 | ||
| 76 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() { | 75 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() { |
| 77 | Set<ClassEntry> out = Sets.newHashSet(); | 76 | Set<ClassEntry> out = Sets.newHashSet(); |
| 78 | out.addAll(matchedSourceFields.keySet()); | 77 | out.addAll(matchedSourceFields.keySet()); |
| 79 | out.removeAll(unmatchedSourceFields.keySet()); | 78 | out.removeAll(unmatchedSourceFields.keySet()); |
| 80 | return out; | 79 | return out; |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | public Collection<FieldEntry> getUnmatchedSourceFields() { | 82 | public Collection<FieldEntry> getUnmatchedSourceFields() { |
| 84 | return unmatchedSourceFields.values(); | 83 | return unmatchedSourceFields.values(); |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) { | 86 | public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) { |
| 88 | return unmatchedSourceFields.get(sourceClass); | 87 | return unmatchedSourceFields.get(sourceClass); |
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | public Collection<FieldEntry> getUnmatchedDestFields() { | 90 | public Collection<FieldEntry> getUnmatchedDestFields() { |
| 92 | return unmatchedDestFields.values(); | 91 | return unmatchedDestFields.values(); |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) { | 94 | public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) { |
| 96 | return unmatchedDestFields.get(destClass); | 95 | return unmatchedDestFields.get(destClass); |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | public Collection<FieldEntry> getUnmatchableSourceFields() { | 98 | public Collection<FieldEntry> getUnmatchableSourceFields() { |
| 100 | return unmatchableSourceFields.values(); | 99 | return unmatchableSourceFields.values(); |
| 101 | } | 100 | } |
| 102 | 101 | ||
| 103 | public boolean hasSource(FieldEntry fieldEntry) { | 102 | public boolean hasSource(FieldEntry fieldEntry) { |
| 104 | return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); | 103 | return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); |
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | public boolean hasDest(FieldEntry fieldEntry) { | 106 | public boolean hasDest(FieldEntry fieldEntry) { |
| 108 | return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); | 107 | return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); |
| 109 | } | 108 | } |
| 110 | 109 | ||
| 111 | public BiMap<FieldEntry, FieldEntry> matches() { | 110 | public BiMap<FieldEntry, FieldEntry> matches() { |
| 112 | return matches; | 111 | return matches; |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 115 | public boolean isMatchedSourceField(FieldEntry sourceField) { | 114 | public boolean isMatchedSourceField(FieldEntry sourceField) { |
| 116 | return matches.containsKey(sourceField); | 115 | return matches.containsKey(sourceField); |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | public boolean isMatchedDestField(FieldEntry destField) { | 118 | public boolean isMatchedDestField(FieldEntry destField) { |
| 120 | return matches.containsValue(destField); | 119 | return matches.containsValue(destField); |
| 121 | } | 120 | } |
| 122 | 121 | ||
| 123 | public void makeMatch(FieldEntry sourceField, FieldEntry destField) { | 122 | public void makeMatch(FieldEntry sourceField, FieldEntry destField) { |
| 124 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 123 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 125 | assert (wasRemoved); | 124 | assert (wasRemoved); |
| 126 | wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); | 125 | wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); |
| 127 | assert (wasRemoved); | 126 | assert (wasRemoved); |
| 128 | addMatch(sourceField, destField); | 127 | addMatch(sourceField, destField); |
| 129 | } | 128 | } |
| 130 | 129 | ||
| 131 | public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { | 130 | public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { |
| 132 | FieldEntry match = matches.get(sourceField); | 131 | FieldEntry match = matches.get(sourceField); |
| 133 | return match != null && match.equals(destField); | 132 | return match != null && match.equals(destField); |
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { | 135 | public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { |
| 137 | boolean wasRemoved = matches.remove(sourceField) != null; | 136 | boolean wasRemoved = matches.remove(sourceField) != null; |
| 138 | assert (wasRemoved); | 137 | assert (wasRemoved); |
| 139 | wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 138 | wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 140 | assert (wasRemoved); | 139 | assert (wasRemoved); |
| 141 | addUnmatchedSourceField(sourceField); | 140 | addUnmatchedSourceField(sourceField); |
| 142 | addUnmatchedDestField(destField); | 141 | addUnmatchedDestField(destField); |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | public void makeSourceUnmatchable(FieldEntry sourceField) { | 144 | public void makeSourceUnmatchable(FieldEntry sourceField) { |
| 146 | assert (!isMatchedSourceField(sourceField)); | 145 | assert (!isMatchedSourceField(sourceField)); |
| 147 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 146 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 148 | assert (wasRemoved); | 147 | assert (wasRemoved); |
| 149 | addUnmatchableSourceField(sourceField); | 148 | addUnmatchableSourceField(sourceField); |
| 150 | } | 149 | } |
| 151 | } | 150 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java index a5ded677..fa3e9362 100644 --- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| @@ -29,688 +30,682 @@ import java.util.jar.JarFile; | |||
| 29 | 30 | ||
| 30 | public class MappingsConverter { | 31 | public class MappingsConverter { |
| 31 | 32 | ||
| 32 | public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { | 33 | public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { |
| 33 | 34 | ||
| 34 | // index jars | 35 | // index jars |
| 35 | System.out.println("Indexing source jar..."); | 36 | System.out.println("Indexing source jar..."); |
| 36 | JarIndex sourceIndex = new JarIndex(); | 37 | JarIndex sourceIndex = new JarIndex(); |
| 37 | sourceIndex.indexJar(sourceJar, false); | 38 | sourceIndex.indexJar(sourceJar, false); |
| 38 | System.out.println("Indexing dest jar..."); | 39 | System.out.println("Indexing dest jar..."); |
| 39 | JarIndex destIndex = new JarIndex(); | 40 | JarIndex destIndex = new JarIndex(); |
| 40 | destIndex.indexJar(destJar, false); | 41 | destIndex.indexJar(destJar, false); |
| 41 | 42 | ||
| 42 | // compute the matching | 43 | // compute the matching |
| 43 | ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); | 44 | ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); |
| 44 | return new ClassMatches(matching.matches()); | 45 | return new ClassMatches(matching.matches()); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { | 48 | public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { |
| 48 | 49 | ||
| 49 | System.out.println("Iteratively matching classes"); | 50 | System.out.println("Iteratively matching classes"); |
| 50 | 51 | ||
| 51 | ClassMatching lastMatching = null; | 52 | ClassMatching lastMatching = null; |
| 52 | int round = 0; | 53 | int round = 0; |
| 53 | SidedClassNamer sourceNamer = null; | 54 | SidedClassNamer sourceNamer = null; |
| 54 | SidedClassNamer destNamer = null; | 55 | SidedClassNamer destNamer = null; |
| 55 | for (boolean useReferences : Arrays.asList(false, true)) { | 56 | for (boolean useReferences : Arrays.asList(false, true)) { |
| 56 | 57 | ||
| 57 | int numUniqueMatchesLastTime = 0; | 58 | int numUniqueMatchesLastTime = 0; |
| 58 | if (lastMatching != null) { | 59 | if (lastMatching != null) { |
| 59 | numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); | 60 | numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | while (true) { | 63 | while (true) { |
| 63 | 64 | ||
| 64 | System.out.println("Round " + (++round) + "..."); | 65 | System.out.println("Round " + (++round) + "..."); |
| 65 | 66 | ||
| 66 | // init the matching with identity settings | 67 | // init the matching with identity settings |
| 67 | ClassMatching matching = new ClassMatching( | 68 | ClassMatching matching = new ClassMatching( |
| 68 | new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), | 69 | new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), |
| 69 | new ClassIdentifier(destJar, destIndex, destNamer, useReferences) | 70 | new ClassIdentifier(destJar, destIndex, destNamer, useReferences) |
| 70 | ); | 71 | ); |
| 71 | 72 | ||
| 72 | if (knownMatches != null) { | 73 | if (knownMatches != null) { |
| 73 | matching.addKnownMatches(knownMatches); | 74 | matching.addKnownMatches(knownMatches); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | if (lastMatching == null) { | 77 | if (lastMatching == null) { |
| 77 | // search all classes | 78 | // search all classes |
| 78 | matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); | 79 | matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); |
| 79 | } else { | 80 | } else { |
| 80 | // we already know about these matches from last time | 81 | // we already know about these matches from last time |
| 81 | matching.addKnownMatches(lastMatching.uniqueMatches()); | 82 | matching.addKnownMatches(lastMatching.uniqueMatches()); |
| 82 | 83 | ||
| 83 | // search unmatched and ambiguously-matched classes | 84 | // search unmatched and ambiguously-matched classes |
| 84 | matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); | 85 | matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); |
| 85 | for (ClassMatch match : lastMatching.ambiguousMatches()) { | 86 | for (ClassMatch match : lastMatching.ambiguousMatches()) { |
| 86 | matching.match(match.sourceClasses, match.destClasses); | 87 | matching.match(match.sourceClasses, match.destClasses); |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 89 | System.out.println(matching); | 90 | System.out.println(matching); |
| 90 | BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); | 91 | BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); |
| 91 | 92 | ||
| 92 | // did we match anything new this time? | 93 | // did we match anything new this time? |
| 93 | if (uniqueMatches.size() > numUniqueMatchesLastTime) { | 94 | if (uniqueMatches.size() > numUniqueMatchesLastTime) { |
| 94 | numUniqueMatchesLastTime = uniqueMatches.size(); | 95 | numUniqueMatchesLastTime = uniqueMatches.size(); |
| 95 | lastMatching = matching; | 96 | lastMatching = matching; |
| 96 | } else { | 97 | } else { |
| 97 | break; | 98 | break; |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | // update the namers | 101 | // update the namers |
| 101 | ClassNamer namer = new ClassNamer(uniqueMatches); | 102 | ClassNamer namer = new ClassNamer(uniqueMatches); |
| 102 | sourceNamer = namer.getSourceNamer(); | 103 | sourceNamer = namer.getSourceNamer(); |
| 103 | destNamer = namer.getDestNamer(); | 104 | destNamer = namer.getDestNamer(); |
| 104 | } | 105 | } |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | return lastMatching; | 108 | return lastMatching; |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) | 111 | public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) |
| 111 | throws MappingConflict { | 112 | throws MappingConflict { |
| 112 | // sort the unique matches by size of inner class chain | 113 | // sort the unique matches by size of inner class chain |
| 113 | Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); | 114 | Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); |
| 114 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { | 115 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { |
| 115 | int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); | 116 | int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); |
| 116 | matchesByDestChainSize.put(chainSize, match); | 117 | matchesByDestChainSize.put(chainSize, match); |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | // build the mappings (in order of small-to-large inner chains) | 120 | // build the mappings (in order of small-to-large inner chains) |
| 120 | Mappings newMappings = new Mappings(); | 121 | Mappings newMappings = new Mappings(); |
| 121 | List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); | 122 | List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); |
| 122 | Collections.sort(chainSizes); | 123 | Collections.sort(chainSizes); |
| 123 | for (int chainSize : chainSizes) { | 124 | for (int chainSize : chainSizes) { |
| 124 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { | 125 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { |
| 125 | // get class info | 126 | // get class info |
| 126 | ClassEntry obfSourceClassEntry = match.getKey(); | 127 | ClassEntry obfSourceClassEntry = match.getKey(); |
| 127 | ClassEntry obfDestClassEntry = match.getValue(); | 128 | ClassEntry obfDestClassEntry = match.getValue(); |
| 128 | List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); | 129 | List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); |
| 129 | 130 | ||
| 130 | ClassMapping sourceMapping; | 131 | ClassMapping sourceMapping; |
| 131 | if (obfSourceClassEntry.isInnerClass()) { | 132 | if (obfSourceClassEntry.isInnerClass()) { |
| 132 | List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); | 133 | List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); |
| 133 | sourceMapping = srcClassChain.get(srcClassChain.size() - 1); | 134 | sourceMapping = srcClassChain.get(srcClassChain.size() - 1); |
| 134 | } else { | 135 | } else { |
| 135 | sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); | 136 | sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | if (sourceMapping == null) { | 139 | if (sourceMapping == null) { |
| 139 | // if this class was never deobfuscated, don't try to match it | 140 | // if this class was never deobfuscated, don't try to match it |
| 140 | continue; | 141 | continue; |
| 141 | } | 142 | } |
| 142 | 143 | ||
| 143 | // find out where to make the dest class mapping | 144 | // find out where to make the dest class mapping |
| 144 | if (destClassChain.size() == 1) { | 145 | if (destClassChain.size() == 1) { |
| 145 | // not an inner class, add directly to mappings | 146 | // not an inner class, add directly to mappings |
| 146 | newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); | 147 | newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); |
| 147 | } else { | 148 | } else { |
| 148 | // inner class, find the outer class mapping | 149 | // inner class, find the outer class mapping |
| 149 | ClassMapping destMapping = null; | 150 | ClassMapping destMapping = null; |
| 150 | for (int i = 0; i < destClassChain.size() - 1; i++) { | 151 | for (int i = 0; i < destClassChain.size() - 1; i++) { |
| 151 | ClassEntry destChainClassEntry = destClassChain.get(i); | 152 | ClassEntry destChainClassEntry = destClassChain.get(i); |
| 152 | if (destMapping == null) { | 153 | if (destMapping == null) { |
| 153 | destMapping = newMappings.getClassByObf(destChainClassEntry); | 154 | destMapping = newMappings.getClassByObf(destChainClassEntry); |
| 154 | if (destMapping == null) { | 155 | if (destMapping == null) { |
| 155 | destMapping = new ClassMapping(destChainClassEntry.getName()); | 156 | destMapping = new ClassMapping(destChainClassEntry.getName()); |
| 156 | newMappings.addClassMapping(destMapping); | 157 | newMappings.addClassMapping(destMapping); |
| 157 | } | 158 | } |
| 158 | } else { | 159 | } else { |
| 159 | destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); | 160 | destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); |
| 160 | if (destMapping == null) { | 161 | if (destMapping == null) { |
| 161 | destMapping = new ClassMapping(destChainClassEntry.getName()); | 162 | destMapping = new ClassMapping(destChainClassEntry.getName()); |
| 162 | destMapping.addInnerClassMapping(destMapping); | 163 | destMapping.addInnerClassMapping(destMapping); |
| 163 | } | 164 | } |
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); | 167 | destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); |
| 167 | } | 168 | } |
| 168 | } | 169 | } |
| 169 | } | 170 | } |
| 170 | return newMappings; | 171 | return newMappings; |
| 171 | } | 172 | } |
| 172 | 173 | ||
| 173 | private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { | 174 | private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { |
| 174 | 175 | ||
| 175 | ClassNameReplacer replacer = className -> | 176 | ClassNameReplacer replacer = className -> |
| 176 | { | 177 | { |
| 177 | ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); | 178 | ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); |
| 178 | if (newClassEntry != null) { | 179 | if (newClassEntry != null) { |
| 179 | return newClassEntry.getName(); | 180 | return newClassEntry.getName(); |
| 180 | } | 181 | } |
| 181 | return null; | 182 | return null; |
| 182 | }; | 183 | }; |
| 183 | 184 | ||
| 184 | ClassMapping newClassMapping; | 185 | ClassMapping newClassMapping; |
| 185 | String deobfName = oldClassMapping.getDeobfName(); | 186 | String deobfName = oldClassMapping.getDeobfName(); |
| 186 | if (deobfName != null) { | 187 | if (deobfName != null) { |
| 187 | if (useSimpleName) { | 188 | if (useSimpleName) { |
| 188 | deobfName = new ClassEntry(deobfName).getSimpleName(); | 189 | deobfName = new ClassEntry(deobfName).getSimpleName(); |
| 189 | } | 190 | } |
| 190 | newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); | 191 | newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); |
| 191 | } else { | 192 | } else { |
| 192 | newClassMapping = new ClassMapping(newObfClass.getName()); | 193 | newClassMapping = new ClassMapping(newObfClass.getName()); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | // migrate fields | 196 | // migrate fields |
| 196 | for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { | 197 | for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { |
| 197 | if (canMigrate(oldFieldMapping.getObfType(), matches)) { | 198 | if (canMigrate(oldFieldMapping.getObfType(), matches)) { |
| 198 | newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); | 199 | newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); |
| 199 | } else { | 200 | } else { |
| 200 | System.out.println(String.format("Can't map field, dropping: %s.%s %s", | 201 | System.out.println(String.format("Can't map field, dropping: %s.%s %s", |
| 201 | oldClassMapping.getDeobfName(), | 202 | oldClassMapping.getDeobfName(), |
| 202 | oldFieldMapping.getDeobfName(), | 203 | oldFieldMapping.getDeobfName(), |
| 203 | oldFieldMapping.getObfType() | 204 | oldFieldMapping.getObfType() |
| 204 | )); | 205 | )); |
| 205 | } | 206 | } |
| 206 | } | 207 | } |
| 207 | 208 | ||
| 208 | // migrate methods | 209 | // migrate methods |
| 209 | for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { | 210 | for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { |
| 210 | if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { | 211 | if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { |
| 211 | newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); | 212 | newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); |
| 212 | } else { | 213 | } else { |
| 213 | System.out.println(String.format("Can't map method, dropping: %s.%s %s", | 214 | System.out.println(String.format("Can't map method, dropping: %s.%s %s", |
| 214 | oldClassMapping.getDeobfName(), | 215 | oldClassMapping.getDeobfName(), |
| 215 | oldMethodMapping.getDeobfName(), | 216 | oldMethodMapping.getDeobfName(), |
| 216 | oldMethodMapping.getObfSignature() | 217 | oldMethodMapping.getObfSignature() |
| 217 | )); | 218 | )); |
| 218 | } | 219 | } |
| 219 | } | 220 | } |
| 220 | 221 | ||
| 221 | return newClassMapping; | 222 | return newClassMapping; |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { | 225 | private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { |
| 225 | for (Type oldObfType : oldObfSignature.types()) { | 226 | for (Type oldObfType : oldObfSignature.types()) { |
| 226 | if (!canMigrate(oldObfType, classMatches)) { | 227 | if (!canMigrate(oldObfType, classMatches)) { |
| 227 | return false; | 228 | return false; |
| 228 | } | 229 | } |
| 229 | } | 230 | } |
| 230 | return true; | 231 | return true; |
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { | 234 | private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { |
| 234 | 235 | ||
| 235 | // non classes can be migrated | 236 | // non classes can be migrated |
| 236 | if (!oldObfType.hasClass()) { | 237 | if (!oldObfType.hasClass()) { |
| 237 | return true; | 238 | return true; |
| 238 | } | 239 | } |
| 239 | 240 | ||
| 240 | // non obfuscated classes can be migrated | 241 | // non obfuscated classes can be migrated |
| 241 | ClassEntry classEntry = oldObfType.getClassEntry(); | 242 | ClassEntry classEntry = oldObfType.getClassEntry(); |
| 242 | if (classEntry.getPackageName() != null) { | 243 | if (classEntry.getPackageName() != null) { |
| 243 | return true; | 244 | return true; |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 246 | // obfuscated classes with mappings can be migrated | 247 | // obfuscated classes with mappings can be migrated |
| 247 | return classMatches.getUniqueMatches().containsKey(classEntry); | 248 | return classMatches.getUniqueMatches().containsKey(classEntry); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { | 251 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { |
| 251 | 252 | ||
| 252 | // sort the changes so classes are renamed in the correct order | 253 | // sort the changes so classes are renamed in the correct order |
| 253 | // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b | 254 | // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b |
| 254 | LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); | 255 | LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); |
| 255 | int numChangesLeft = changes.size(); | 256 | int numChangesLeft = changes.size(); |
| 256 | while (!changes.isEmpty()) { | 257 | while (!changes.isEmpty()) { |
| 257 | Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); | 258 | Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); |
| 258 | while (iter.hasNext()) { | 259 | while (iter.hasNext()) { |
| 259 | Map.Entry<ClassEntry, ClassEntry> change = iter.next(); | 260 | Map.Entry<ClassEntry, ClassEntry> change = iter.next(); |
| 260 | if (changes.containsKey(change.getValue())) { | 261 | if (changes.containsKey(change.getValue())) { |
| 261 | sortedChanges.put(change.getKey(), change.getValue()); | 262 | sortedChanges.put(change.getKey(), change.getValue()); |
| 262 | iter.remove(); | 263 | iter.remove(); |
| 263 | } | 264 | } |
| 264 | } | 265 | } |
| 265 | 266 | ||
| 266 | // did we remove any changes? | 267 | // did we remove any changes? |
| 267 | if (numChangesLeft - changes.size() > 0) { | 268 | if (numChangesLeft - changes.size() > 0) { |
| 268 | // keep going | 269 | // keep going |
| 269 | numChangesLeft = changes.size(); | 270 | numChangesLeft = changes.size(); |
| 270 | } else { | 271 | } else { |
| 271 | // can't sort anymore. There must be a loop | 272 | // can't sort anymore. There must be a loop |
| 272 | break; | 273 | break; |
| 273 | } | 274 | } |
| 274 | } | 275 | } |
| 275 | if (!changes.isEmpty()) { | 276 | if (!changes.isEmpty()) { |
| 276 | throw new Error("Unable to sort class changes! There must be a cycle."); | 277 | throw new Error("Unable to sort class changes! There must be a cycle."); |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | // convert the mappings in the correct class order | 280 | // convert the mappings in the correct class order |
| 280 | for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { | 281 | for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { |
| 281 | mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); | 282 | mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); |
| 282 | } | 283 | } |
| 283 | } | 284 | } |
| 284 | 285 | ||
| 285 | public interface Doer<T extends Entry> { | 286 | public static Doer<FieldEntry> getFieldDoer() { |
| 286 | Collection<T> getDroppedEntries(MappingsChecker checker); | 287 | return new Doer<FieldEntry>() { |
| 287 | 288 | ||
| 288 | Collection<T> getObfEntries(JarIndex jarIndex); | 289 | @Override |
| 289 | 290 | public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { | |
| 290 | Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); | 291 | return checker.getDroppedFieldMappings().keySet(); |
| 291 | 292 | } | |
| 292 | Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); | 293 | |
| 293 | 294 | @Override | |
| 294 | void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); | 295 | public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { |
| 295 | 296 | return jarIndex.getObfFieldEntries(); | |
| 296 | boolean hasObfMember(ClassMapping classMapping, T obfEntry); | 297 | } |
| 297 | 298 | ||
| 298 | void removeMemberByObf(ClassMapping classMapping, T obfEntry); | 299 | @Override |
| 299 | } | 300 | public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { |
| 300 | 301 | return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); | |
| 301 | public static Doer<FieldEntry> getFieldDoer() { | 302 | } |
| 302 | return new Doer<FieldEntry>() { | 303 | |
| 303 | 304 | @Override | |
| 304 | @Override | 305 | public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { |
| 305 | public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { | 306 | Set<FieldEntry> out = Sets.newHashSet(); |
| 306 | return checker.getDroppedFieldMappings().keySet(); | 307 | for (FieldEntry obfDestField : obfDestFields) { |
| 307 | } | 308 | Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); |
| 308 | 309 | if (translatedDestType.equals(obfSourceField.getType())) { | |
| 309 | @Override | 310 | out.add(obfDestField); |
| 310 | public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { | 311 | } |
| 311 | return jarIndex.getObfFieldEntries(); | 312 | } |
| 312 | } | 313 | return out; |
| 313 | 314 | } | |
| 314 | @Override | 315 | |
| 315 | public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { | 316 | @Override |
| 316 | return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); | 317 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { |
| 317 | } | 318 | FieldMapping fieldMapping = (FieldMapping) memberMapping; |
| 318 | 319 | classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); | |
| 319 | @Override | 320 | } |
| 320 | public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { | 321 | |
| 321 | Set<FieldEntry> out = Sets.newHashSet(); | 322 | @Override |
| 322 | for (FieldEntry obfDestField : obfDestFields) { | 323 | public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { |
| 323 | Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); | 324 | return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; |
| 324 | if (translatedDestType.equals(obfSourceField.getType())) { | 325 | } |
| 325 | out.add(obfDestField); | 326 | |
| 326 | } | 327 | @Override |
| 327 | } | 328 | public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { |
| 328 | return out; | 329 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); |
| 329 | } | 330 | } |
| 330 | 331 | }; | |
| 331 | @Override | 332 | } |
| 332 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { | 333 | |
| 333 | FieldMapping fieldMapping = (FieldMapping) memberMapping; | 334 | public static Doer<BehaviorEntry> getMethodDoer() { |
| 334 | classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); | 335 | return new Doer<BehaviorEntry>() { |
| 335 | } | 336 | |
| 336 | 337 | @Override | |
| 337 | @Override | 338 | public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { |
| 338 | public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { | 339 | return checker.getDroppedMethodMappings().keySet(); |
| 339 | return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; | 340 | } |
| 340 | } | 341 | |
| 341 | 342 | @Override | |
| 342 | @Override | 343 | public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { |
| 343 | public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { | 344 | return jarIndex.getObfBehaviorEntries(); |
| 344 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); | 345 | } |
| 345 | } | 346 | |
| 346 | }; | 347 | @Override |
| 347 | } | 348 | public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { |
| 348 | 349 | return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); | |
| 349 | public static Doer<BehaviorEntry> getMethodDoer() { | 350 | } |
| 350 | return new Doer<BehaviorEntry>() { | 351 | |
| 351 | 352 | @Override | |
| 352 | @Override | 353 | public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { |
| 353 | public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { | 354 | Set<BehaviorEntry> out = Sets.newHashSet(); |
| 354 | return checker.getDroppedMethodMappings().keySet(); | 355 | for (BehaviorEntry obfDestField : obfDestFields) { |
| 355 | } | 356 | // Try to translate the signature |
| 356 | 357 | Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); | |
| 357 | @Override | 358 | if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) |
| 358 | public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { | 359 | out.add(obfDestField); |
| 359 | return jarIndex.getObfBehaviorEntries(); | 360 | } |
| 360 | } | 361 | return out; |
| 361 | 362 | } | |
| 362 | @Override | 363 | |
| 363 | public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { | 364 | @Override |
| 364 | return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); | 365 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { |
| 365 | } | 366 | MethodMapping methodMapping = (MethodMapping) memberMapping; |
| 366 | 367 | classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); | |
| 367 | @Override | 368 | } |
| 368 | public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { | 369 | |
| 369 | Set<BehaviorEntry> out = Sets.newHashSet(); | 370 | @Override |
| 370 | for (BehaviorEntry obfDestField : obfDestFields) { | 371 | public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { |
| 371 | // Try to translate the signature | 372 | return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; |
| 372 | Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); | 373 | } |
| 373 | if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) | 374 | |
| 374 | out.add(obfDestField); | 375 | @Override |
| 375 | } | 376 | public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { |
| 376 | return out; | 377 | classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); |
| 377 | } | 378 | } |
| 378 | 379 | }; | |
| 379 | @Override | 380 | } |
| 380 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { | 381 | |
| 381 | MethodMapping methodMapping = (MethodMapping) memberMapping; | 382 | public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) { |
| 382 | classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); | 383 | int sourcePos = 0; |
| 383 | } | 384 | int destPos = 0; |
| 384 | 385 | while (sourceIt.hasNext() && destIt.hasNext()) { | |
| 385 | @Override | 386 | try { |
| 386 | public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { | 387 | sourcePos = sourceIt.next(); |
| 387 | return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; | 388 | destPos = destIt.next(); |
| 388 | } | 389 | if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) |
| 389 | 390 | return sourcePos; | |
| 390 | @Override | 391 | } catch (BadBytecode badBytecode) { |
| 391 | public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { | 392 | // Ignore bad bytecode (it might be a little bit dangerous...) |
| 392 | classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); | 393 | } |
| 393 | } | 394 | } |
| 394 | }; | 395 | if (sourcePos < destPos) |
| 395 | } | 396 | return sourcePos; |
| 396 | 397 | else if (destPos < sourcePos) | |
| 397 | public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) | 398 | return destPos; |
| 398 | { | 399 | return sourcePos; |
| 399 | int sourcePos = 0; | 400 | } |
| 400 | int destPos = 0; | 401 | |
| 401 | while (sourceIt.hasNext() && destIt.hasNext()) | 402 | public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, |
| 402 | { | 403 | Set<BehaviorEntry> obfDestEntries) { |
| 403 | try | 404 | try { |
| 404 | { | 405 | // Get the source method with Javassist |
| 405 | sourcePos = sourceIt.next(); | 406 | CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); |
| 406 | destPos = destIt.next(); | 407 | CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); |
| 407 | if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) | 408 | |
| 408 | return sourcePos; | 409 | // Empty method body, ignore! |
| 409 | } catch (BadBytecode badBytecode) | 410 | if (sourceAttribute == null) |
| 410 | { | 411 | return null; |
| 411 | // Ignore bad bytecode (it might be a little bit dangerous...) | 412 | for (BehaviorEntry desEntry : obfDestEntries) { |
| 412 | } | 413 | try { |
| 413 | } | 414 | CtMethod destCtClassMethod = destCtClass |
| 414 | if (sourcePos < destPos) | 415 | .getMethod(desEntry.getName(), desEntry.getSignature().toString()); |
| 415 | return sourcePos; | 416 | CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); |
| 416 | else if (destPos < sourcePos) | 417 | |
| 417 | return destPos; | 418 | // Ignore empty body methods |
| 418 | return sourcePos; | 419 | if (destAttribute == null) |
| 419 | } | 420 | continue; |
| 420 | 421 | CodeIterator destIterator = destAttribute.iterator(); | |
| 421 | public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, | 422 | int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); |
| 422 | Set<BehaviorEntry> obfDestEntries) | 423 | |
| 423 | { | 424 | // The bytecode is identical to the original method, assuming that the method is correct! |
| 424 | try | 425 | if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) |
| 425 | { | 426 | return desEntry; |
| 426 | // Get the source method with Javassist | 427 | } catch (NotFoundException e) { |
| 427 | CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); | 428 | e.printStackTrace(); |
| 428 | CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); | 429 | } |
| 429 | 430 | } | |
| 430 | // Empty method body, ignore! | 431 | } catch (NotFoundException e) { |
| 431 | if (sourceAttribute == null) | 432 | e.printStackTrace(); |
| 432 | return null; | 433 | return null; |
| 433 | for (BehaviorEntry desEntry : obfDestEntries) | 434 | } |
| 434 | { | 435 | return null; |
| 435 | try | 436 | } |
| 436 | { | 437 | |
| 437 | CtMethod destCtClassMethod = destCtClass | 438 | public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, |
| 438 | .getMethod(desEntry.getName(), desEntry.getSignature().toString()); | 439 | Mappings destMappings, |
| 439 | CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); | 440 | Deobfuscator sourceDeobfuscator, |
| 440 | 441 | Mappings sourceMappings, | |
| 441 | // Ignore empty body methods | 442 | ClassMatches classMatches, |
| 442 | if (destAttribute == null) | 443 | Doer<BehaviorEntry> doer) { |
| 443 | continue; | 444 | |
| 444 | CodeIterator destIterator = destAttribute.iterator(); | 445 | MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); |
| 445 | int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); | 446 | |
| 446 | 447 | // unmatched source fields are easy | |
| 447 | // The bytecode is identical to the original method, assuming that the method is correct! | 448 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); |
| 448 | if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) | 449 | checker.dropBrokenMappings(destMappings); |
| 449 | return desEntry; | 450 | for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { |
| 450 | } catch (NotFoundException e) | 451 | BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); |
| 451 | { | 452 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); |
| 452 | e.printStackTrace(); | 453 | } |
| 453 | } | 454 | |
| 454 | } | 455 | // get matched fields (anything that's left after the checks/drops is matched( |
| 455 | } catch (NotFoundException e) | 456 | for (ClassMapping classMapping : destMappings.classes()) |
| 456 | { | 457 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); |
| 457 | e.printStackTrace(); | 458 | |
| 458 | return null; | 459 | // get unmatched dest fields |
| 459 | } | 460 | doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() |
| 460 | return null; | 461 | .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) |
| 461 | } | 462 | .forEach(memberMatches::addUnmatchedDestEntry); |
| 462 | 463 | ||
| 463 | public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, Mappings destMappings, Deobfuscator sourceDeobfuscator, Mappings sourceMappings, ClassMatches classMatches, Doer<BehaviorEntry> doer) { | 464 | // Apply mappings to deobfuscator |
| 464 | 465 | ||
| 465 | MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); | 466 | // Create type loader |
| 466 | 467 | TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); | |
| 467 | // unmatched source fields are easy | 468 | TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); |
| 468 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 469 | |
| 469 | checker.dropBrokenMappings(destMappings); | 470 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); |
| 470 | for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { | 471 | |
| 471 | BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); | 472 | // go through the unmatched source fields and try to pick out the easy matches |
| 472 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); | 473 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { |
| 473 | } | 474 | for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { |
| 474 | 475 | ||
| 475 | // get matched fields (anything that's left after the checks/drops is matched( | 476 | // get the possible dest matches |
| 476 | for (ClassMapping classMapping : destMappings.classes()) | 477 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); |
| 477 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); | 478 | |
| 478 | 479 | // filter by type/signature | |
| 479 | // get unmatched dest fields | 480 | Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); |
| 480 | doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() | 481 | |
| 481 | .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) | 482 | if (obfDestEntries.size() == 1) { |
| 482 | .forEach(memberMatches::addUnmatchedDestEntry); | 483 | // make the easy match |
| 483 | 484 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | |
| 484 | // Apply mappings to deobfuscator | 485 | } else if (obfDestEntries.isEmpty()) { |
| 485 | 486 | // no match is possible =( | |
| 486 | // Create type loader | 487 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); |
| 487 | TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); | 488 | } else { |
| 488 | TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); | 489 | // Multiple matches! Scan methods instructions |
| 489 | 490 | CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); | |
| 490 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); | 491 | CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); |
| 491 | 492 | BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); | |
| 492 | // go through the unmatched source fields and try to pick out the easy matches | 493 | // the method match correctly, match it on the member mapping! |
| 493 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { | 494 | if (match != null) |
| 494 | for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { | 495 | memberMatches.makeMatch(obfSourceEntry, match); |
| 495 | 496 | } | |
| 496 | // get the possible dest matches | 497 | } |
| 497 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 498 | } |
| 498 | 499 | ||
| 499 | // filter by type/signature | 500 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", |
| 500 | Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); | 501 | memberMatches.getUnmatchedSourceEntries().size(), |
| 501 | 502 | memberMatches.getUnmatchableSourceEntries().size() | |
| 502 | if (obfDestEntries.size() == 1) { | 503 | )); |
| 503 | // make the easy match | 504 | |
| 504 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | 505 | return memberMatches; |
| 505 | } else if (obfDestEntries.isEmpty()) { | 506 | } |
| 506 | // no match is possible =( | 507 | |
| 507 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); | 508 | public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { |
| 508 | } else | 509 | |
| 509 | { | 510 | MemberMatches<T> memberMatches = new MemberMatches<>(); |
| 510 | // Multiple matches! Scan methods instructions | 511 | |
| 511 | CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); | 512 | // unmatched source fields are easy |
| 512 | CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); | 513 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); |
| 513 | BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); | 514 | checker.dropBrokenMappings(destMappings); |
| 514 | // the method match correctly, match it on the member mapping! | 515 | for (T destObfEntry : doer.getDroppedEntries(checker)) { |
| 515 | if (match != null) | 516 | T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); |
| 516 | memberMatches.makeMatch(obfSourceEntry, match); | 517 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); |
| 517 | } | 518 | } |
| 518 | } | 519 | |
| 519 | } | 520 | // get matched fields (anything that's left after the checks/drops is matched( |
| 520 | 521 | for (ClassMapping classMapping : destMappings.classes()) { | |
| 521 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", | 522 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); |
| 522 | memberMatches.getUnmatchedSourceEntries().size(), | 523 | } |
| 523 | memberMatches.getUnmatchableSourceEntries().size() | 524 | |
| 524 | )); | 525 | // get unmatched dest fields |
| 525 | 526 | for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { | |
| 526 | return memberMatches; | 527 | if (!memberMatches.isMatchedDestEntry(destEntry)) { |
| 527 | } | 528 | memberMatches.addUnmatchedDestEntry(destEntry); |
| 528 | 529 | } | |
| 529 | public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { | 530 | } |
| 530 | 531 | ||
| 531 | MemberMatches<T> memberMatches = new MemberMatches<>(); | 532 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); |
| 532 | 533 | ||
| 533 | // unmatched source fields are easy | 534 | // go through the unmatched source fields and try to pick out the easy matches |
| 534 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 535 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { |
| 535 | checker.dropBrokenMappings(destMappings); | 536 | for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { |
| 536 | for (T destObfEntry : doer.getDroppedEntries(checker)) { | 537 | |
| 537 | T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); | 538 | // get the possible dest matches |
| 538 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); | 539 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); |
| 539 | } | 540 | |
| 540 | 541 | // filter by type/signature | |
| 541 | // get matched fields (anything that's left after the checks/drops is matched( | 542 | Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); |
| 542 | for (ClassMapping classMapping : destMappings.classes()) { | 543 | |
| 543 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); | 544 | if (obfDestEntries.size() == 1) { |
| 544 | } | 545 | // make the easy match |
| 545 | 546 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | |
| 546 | // get unmatched dest fields | 547 | } else if (obfDestEntries.isEmpty()) { |
| 547 | for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { | 548 | // no match is possible =( |
| 548 | if (!memberMatches.isMatchedDestEntry(destEntry)) { | 549 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); |
| 549 | memberMatches.addUnmatchedDestEntry(destEntry); | 550 | } |
| 550 | } | 551 | } |
| 551 | } | 552 | } |
| 552 | 553 | ||
| 553 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); | 554 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", |
| 554 | 555 | memberMatches.getUnmatchedSourceEntries().size(), | |
| 555 | // go through the unmatched source fields and try to pick out the easy matches | 556 | memberMatches.getUnmatchableSourceEntries().size() |
| 556 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { | 557 | )); |
| 557 | for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { | 558 | |
| 558 | 559 | return memberMatches; | |
| 559 | // get the possible dest matches | 560 | } |
| 560 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 561 | |
| 561 | 562 | private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { | |
| 562 | // filter by type/signature | 563 | |
| 563 | Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); | 564 | // get the fields for this class |
| 564 | 565 | for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { | |
| 565 | if (obfDestEntries.size() == 1) { | 566 | T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); |
| 566 | // make the easy match | 567 | T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); |
| 567 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | 568 | memberMatches.addMatch(srcObfField, destObfField); |
| 568 | } else if (obfDestEntries.isEmpty()) { | 569 | } |
| 569 | // no match is possible =( | 570 | |
| 570 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); | 571 | // recurse |
| 571 | } | 572 | for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { |
| 572 | } | 573 | collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); |
| 573 | } | 574 | } |
| 574 | 575 | } | |
| 575 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", | 576 | |
| 576 | memberMatches.getUnmatchedSourceEntries().size(), | 577 | @SuppressWarnings("unchecked") |
| 577 | memberMatches.getUnmatchableSourceEntries().size() | 578 | private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { |
| 578 | )); | 579 | if (in instanceof FieldEntry) { |
| 579 | 580 | return (T) new FieldEntry( | |
| 580 | return memberMatches; | 581 | map.get(in.getClassEntry()), |
| 581 | } | 582 | in.getName(), |
| 582 | 583 | translate(((FieldEntry) in).getType(), map) | |
| 583 | private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { | 584 | ); |
| 584 | 585 | } else if (in instanceof MethodEntry) { | |
| 585 | // get the fields for this class | 586 | return (T) new MethodEntry( |
| 586 | for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { | 587 | map.get(in.getClassEntry()), |
| 587 | T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); | 588 | in.getName(), |
| 588 | T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); | 589 | translate(((MethodEntry) in).getSignature(), map) |
| 589 | memberMatches.addMatch(srcObfField, destObfField); | 590 | ); |
| 590 | } | 591 | } else if (in instanceof ConstructorEntry) { |
| 591 | 592 | return (T) new ConstructorEntry( | |
| 592 | // recurse | 593 | map.get(in.getClassEntry()), |
| 593 | for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { | 594 | translate(((ConstructorEntry) in).getSignature(), map) |
| 594 | collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); | 595 | ); |
| 595 | } | 596 | } |
| 596 | } | 597 | throw new Error("Unhandled entry type: " + in.getClass()); |
| 597 | 598 | } | |
| 598 | @SuppressWarnings("unchecked") | 599 | |
| 599 | private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { | 600 | private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { |
| 600 | if (in instanceof FieldEntry) { | 601 | return new Type(type, inClassName -> |
| 601 | return (T) new FieldEntry( | 602 | { |
| 602 | map.get(in.getClassEntry()), | 603 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); |
| 603 | in.getName(), | 604 | if (outClassEntry == null) { |
| 604 | translate(((FieldEntry) in).getType(), map) | 605 | return null; |
| 605 | ); | 606 | } |
| 606 | } else if (in instanceof MethodEntry) { | 607 | return outClassEntry.getName(); |
| 607 | return (T) new MethodEntry( | 608 | }); |
| 608 | map.get(in.getClassEntry()), | 609 | } |
| 609 | in.getName(), | 610 | |
| 610 | translate(((MethodEntry) in).getSignature(), map) | 611 | private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { |
| 611 | ); | 612 | if (signature == null) { |
| 612 | } else if (in instanceof ConstructorEntry) { | 613 | return null; |
| 613 | return (T) new ConstructorEntry( | 614 | } |
| 614 | map.get(in.getClassEntry()), | 615 | return new Signature(signature, inClassName -> |
| 615 | translate(((ConstructorEntry) in).getSignature(), map) | 616 | { |
| 616 | ); | 617 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); |
| 617 | } | 618 | if (outClassEntry == null) { |
| 618 | throw new Error("Unhandled entry type: " + in.getClass()); | 619 | return null; |
| 619 | } | 620 | } |
| 620 | 621 | return outClassEntry.getName(); | |
| 621 | private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { | 622 | }); |
| 622 | return new Type(type, inClassName -> | 623 | } |
| 623 | { | 624 | |
| 624 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); | 625 | public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { |
| 625 | if (outClassEntry == null) { | 626 | for (ClassMapping classMapping : mappings.classes()) { |
| 626 | return null; | 627 | applyMemberMatches(classMapping, classMatches, memberMatches, doer); |
| 627 | } | 628 | } |
| 628 | return outClassEntry.getName(); | 629 | } |
| 629 | }); | 630 | |
| 630 | } | 631 | private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { |
| 631 | 632 | ||
| 632 | private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { | 633 | // get the classes |
| 633 | if (signature == null) { | 634 | ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); |
| 634 | return null; | 635 | |
| 635 | } | 636 | // make a map of all the renames we need to make |
| 636 | return new Signature(signature, inClassName -> | 637 | Map<T, T> renames = Maps.newHashMap(); |
| 637 | { | 638 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { |
| 638 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); | 639 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); |
| 639 | if (outClassEntry == null) { | 640 | T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); |
| 640 | return null; | 641 | |
| 641 | } | 642 | // but drop the unmatchable things |
| 642 | return outClassEntry.getName(); | 643 | if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { |
| 643 | }); | 644 | doer.removeMemberByObf(classMapping, obfOldDestEntry); |
| 644 | } | 645 | continue; |
| 645 | 646 | } | |
| 646 | public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { | 647 | |
| 647 | for (ClassMapping classMapping : mappings.classes()) { | 648 | T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); |
| 648 | applyMemberMatches(classMapping, classMatches, memberMatches, doer); | 649 | if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { |
| 649 | } | 650 | renames.put(obfOldDestEntry, obfNewDestEntry); |
| 650 | } | 651 | } |
| 651 | 652 | } | |
| 652 | private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { | 653 | |
| 653 | 654 | if (!renames.isEmpty()) { | |
| 654 | // get the classes | 655 | |
| 655 | ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); | 656 | // apply to this class (should never need more than n passes) |
| 656 | 657 | int numRenamesAppliedThisRound; | |
| 657 | // make a map of all the renames we need to make | 658 | do { |
| 658 | Map<T, T> renames = Maps.newHashMap(); | 659 | numRenamesAppliedThisRound = 0; |
| 659 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { | 660 | |
| 660 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); | 661 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { |
| 661 | T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); | 662 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); |
| 662 | 663 | T obfNewDestEntry = renames.get(obfOldDestEntry); | |
| 663 | // but drop the unmatchable things | 664 | if (obfNewDestEntry != null) { |
| 664 | if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { | 665 | // make sure this rename won't cause a collision |
| 665 | doer.removeMemberByObf(classMapping, obfOldDestEntry); | 666 | // otherwise, save it for the next round and try again next time |
| 666 | continue; | 667 | if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { |
| 667 | } | 668 | doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); |
| 668 | 669 | renames.remove(obfOldDestEntry); | |
| 669 | T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); | 670 | numRenamesAppliedThisRound++; |
| 670 | if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { | 671 | } |
| 671 | renames.put(obfOldDestEntry, obfNewDestEntry); | 672 | } |
| 672 | } | 673 | } |
| 673 | } | 674 | } while (numRenamesAppliedThisRound > 0); |
| 674 | 675 | ||
| 675 | if (!renames.isEmpty()) { | 676 | if (!renames.isEmpty()) { |
| 676 | 677 | System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", | |
| 677 | // apply to this class (should never need more than n passes) | 678 | classMapping.getObfFullName(), renames.size() |
| 678 | int numRenamesAppliedThisRound; | 679 | )); |
| 679 | do { | 680 | for (Map.Entry<T, T> entry : renames.entrySet()) { |
| 680 | numRenamesAppliedThisRound = 0; | 681 | System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); |
| 681 | 682 | } | |
| 682 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { | 683 | } |
| 683 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); | 684 | } |
| 684 | T obfNewDestEntry = renames.get(obfOldDestEntry); | 685 | |
| 685 | if (obfNewDestEntry != null) { | 686 | // recurse |
| 686 | // make sure this rename won't cause a collision | 687 | for (ClassMapping innerClassMapping : classMapping.innerClasses()) { |
| 687 | // otherwise, save it for the next round and try again next time | 688 | applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); |
| 688 | if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { | 689 | } |
| 689 | doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); | 690 | } |
| 690 | renames.remove(obfOldDestEntry); | 691 | |
| 691 | numRenamesAppliedThisRound++; | 692 | private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { |
| 692 | } | 693 | return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); |
| 693 | } | 694 | } |
| 694 | } | 695 | |
| 695 | } while (numRenamesAppliedThisRound > 0); | 696 | public interface Doer<T extends Entry> { |
| 696 | 697 | Collection<T> getDroppedEntries(MappingsChecker checker); | |
| 697 | if (!renames.isEmpty()) { | 698 | |
| 698 | System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", | 699 | Collection<T> getObfEntries(JarIndex jarIndex); |
| 699 | classMapping.getObfFullName(), renames.size() | 700 | |
| 700 | )); | 701 | Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); |
| 701 | for (Map.Entry<T, T> entry : renames.entrySet()) { | 702 | |
| 702 | System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); | 703 | Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); |
| 703 | } | 704 | |
| 704 | } | 705 | void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); |
| 705 | } | 706 | |
| 706 | 707 | boolean hasObfMember(ClassMapping classMapping, T obfEntry); | |
| 707 | // recurse | 708 | |
| 708 | for (ClassMapping innerClassMapping : classMapping.innerClasses()) { | 709 | void removeMemberByObf(ClassMapping classMapping, T obfEntry); |
| 709 | applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); | 710 | } |
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { | ||
| 714 | return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); | ||
| 715 | } | ||
| 716 | } | 711 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesReader.java b/src/main/java/cuchaz/enigma/convert/MatchesReader.java index d86d6c2a..1cf50fa4 100644 --- a/src/main/java/cuchaz/enigma/convert/MatchesReader.java +++ b/src/main/java/cuchaz/enigma/convert/MatchesReader.java | |||
| @@ -8,99 +8,98 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.*; | ||
| 14 | 16 | ||
| 15 | import java.io.*; | 17 | import java.io.*; |
| 16 | import java.nio.charset.Charset; | 18 | import java.nio.charset.Charset; |
| 17 | import java.util.Collection; | 19 | import java.util.Collection; |
| 18 | import java.util.List; | 20 | import java.util.List; |
| 19 | 21 | ||
| 20 | import cuchaz.enigma.mapping.*; | ||
| 21 | |||
| 22 | |||
| 23 | public class MatchesReader { | 22 | public class MatchesReader { |
| 24 | 23 | ||
| 25 | public static ClassMatches readClasses(File file) | 24 | public static ClassMatches readClasses(File file) |
| 26 | throws IOException { | 25 | throws IOException { |
| 27 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { | 26 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { |
| 28 | ClassMatches matches = new ClassMatches(); | 27 | ClassMatches matches = new ClassMatches(); |
| 29 | String line; | 28 | String line; |
| 30 | while ((line = in.readLine()) != null) { | 29 | while ((line = in.readLine()) != null) { |
| 31 | matches.add(readClassMatch(line)); | 30 | matches.add(readClassMatch(line)); |
| 32 | } | 31 | } |
| 33 | return matches; | 32 | return matches; |
| 34 | } | 33 | } |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | private static ClassMatch readClassMatch(String line) { | 36 | private static ClassMatch readClassMatch(String line) { |
| 38 | String[] sides = line.split(":", 2); | 37 | String[] sides = line.split(":", 2); |
| 39 | return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); | 38 | return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | private static Collection<ClassEntry> readClasses(String in) { | 41 | private static Collection<ClassEntry> readClasses(String in) { |
| 43 | List<ClassEntry> entries = Lists.newArrayList(); | 42 | List<ClassEntry> entries = Lists.newArrayList(); |
| 44 | for (String className : in.split(",")) { | 43 | for (String className : in.split(",")) { |
| 45 | className = className.trim(); | 44 | className = className.trim(); |
| 46 | if (className.length() > 0) { | 45 | if (!className.isEmpty()) { |
| 47 | entries.add(new ClassEntry(className)); | 46 | entries.add(new ClassEntry(className)); |
| 48 | } | 47 | } |
| 49 | } | 48 | } |
| 50 | return entries; | 49 | return entries; |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public static <T extends Entry> MemberMatches<T> readMembers(File file) | 52 | public static <T extends Entry> MemberMatches<T> readMembers(File file) |
| 54 | throws IOException { | 53 | throws IOException { |
| 55 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { | 54 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { |
| 56 | MemberMatches<T> matches = new MemberMatches<>(); | 55 | MemberMatches<T> matches = new MemberMatches<>(); |
| 57 | String line; | 56 | String line; |
| 58 | while ((line = in.readLine()) != null) { | 57 | while ((line = in.readLine()) != null) { |
| 59 | readMemberMatch(matches, line); | 58 | readMemberMatch(matches, line); |
| 60 | } | 59 | } |
| 61 | return matches; | 60 | return matches; |
| 62 | } | 61 | } |
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) { | 64 | private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) { |
| 66 | if (line.startsWith("!")) { | 65 | if (line.startsWith("!")) { |
| 67 | T source = readEntry(line.substring(1)); | 66 | T source = readEntry(line.substring(1)); |
| 68 | matches.addUnmatchableSourceEntry(source); | 67 | matches.addUnmatchableSourceEntry(source); |
| 69 | } else { | 68 | } else { |
| 70 | String[] parts = line.split(":", 2); | 69 | String[] parts = line.split(":", 2); |
| 71 | T source = readEntry(parts[0]); | 70 | T source = readEntry(parts[0]); |
| 72 | T dest = readEntry(parts[1]); | 71 | T dest = readEntry(parts[1]); |
| 73 | if (source != null && dest != null) { | 72 | if (source != null && dest != null) { |
| 74 | matches.addMatch(source, dest); | 73 | matches.addMatch(source, dest); |
| 75 | } else if (source != null) { | 74 | } else if (source != null) { |
| 76 | matches.addUnmatchedSourceEntry(source); | 75 | matches.addUnmatchedSourceEntry(source); |
| 77 | } else if (dest != null) { | 76 | } else if (dest != null) { |
| 78 | matches.addUnmatchedDestEntry(dest); | 77 | matches.addUnmatchedDestEntry(dest); |
| 79 | } | 78 | } |
| 80 | } | 79 | } |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | @SuppressWarnings("unchecked") | 82 | @SuppressWarnings("unchecked") |
| 84 | private static <T extends Entry> T readEntry(String in) { | 83 | private static <T extends Entry> T readEntry(String in) { |
| 85 | if (in.length() <= 0) { | 84 | if (in.length() <= 0) { |
| 86 | return null; | 85 | return null; |
| 87 | } | 86 | } |
| 88 | String[] parts = in.split(" "); | 87 | String[] parts = in.split(" "); |
| 89 | if (parts.length == 3 && parts[2].indexOf('(') < 0) { | 88 | if (parts.length == 3 && parts[2].indexOf('(') < 0) { |
| 90 | return (T) new FieldEntry( | 89 | return (T) new FieldEntry( |
| 91 | new ClassEntry(parts[0]), | 90 | new ClassEntry(parts[0]), |
| 92 | parts[1], | 91 | parts[1], |
| 93 | new Type(parts[2]) | 92 | new Type(parts[2]) |
| 94 | ); | 93 | ); |
| 95 | } else { | 94 | } else { |
| 96 | assert (parts.length == 2 || parts.length == 3); | 95 | assert (parts.length == 2 || parts.length == 3); |
| 97 | if (parts.length == 2) { | 96 | if (parts.length == 2) { |
| 98 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); | 97 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); |
| 99 | } else if (parts.length == 3) { | 98 | } else if (parts.length == 3) { |
| 100 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); | 99 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); |
| 101 | } else { | 100 | } else { |
| 102 | throw new Error("Malformed behavior entry: " + in); | 101 | throw new Error("Malformed behavior entry: " + in); |
| 103 | } | 102 | } |
| 104 | } | 103 | } |
| 105 | } | 104 | } |
| 106 | } | 105 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java index dccbf6f1..8fe73265 100644 --- a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java | |||
| @@ -8,113 +8,116 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.convert; | ||
| 12 | 11 | ||
| 13 | import java.io.*; | 12 | package cuchaz.enigma.convert; |
| 14 | import java.nio.charset.Charset; | ||
| 15 | import java.util.Map; | ||
| 16 | 13 | ||
| 17 | import cuchaz.enigma.mapping.BehaviorEntry; | 14 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.Entry; | 16 | import cuchaz.enigma.mapping.Entry; |
| 20 | import cuchaz.enigma.mapping.FieldEntry; | 17 | import cuchaz.enigma.mapping.FieldEntry; |
| 21 | 18 | ||
| 19 | import java.io.File; | ||
| 20 | import java.io.FileOutputStream; | ||
| 21 | import java.io.IOException; | ||
| 22 | import java.io.OutputStreamWriter; | ||
| 23 | import java.nio.charset.Charset; | ||
| 24 | import java.util.Map; | ||
| 22 | 25 | ||
| 23 | public class MatchesWriter { | 26 | public class MatchesWriter { |
| 24 | 27 | ||
| 25 | public static void writeClasses(ClassMatches matches, File file) | 28 | public static void writeClasses(ClassMatches matches, File file) |
| 26 | throws IOException { | 29 | throws IOException { |
| 27 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { | 30 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { |
| 28 | for (ClassMatch match : matches) { | 31 | for (ClassMatch match : matches) { |
| 29 | writeClassMatch(out, match); | 32 | writeClassMatch(out, match); |
| 30 | } | 33 | } |
| 31 | } | 34 | } |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) | 37 | private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) |
| 35 | throws IOException { | 38 | throws IOException { |
| 36 | writeClasses(out, match.sourceClasses); | 39 | writeClasses(out, match.sourceClasses); |
| 37 | out.write(":"); | 40 | out.write(":"); |
| 38 | writeClasses(out, match.destClasses); | 41 | writeClasses(out, match.destClasses); |
| 39 | out.write("\n"); | 42 | out.write("\n"); |
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes) | 45 | private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes) |
| 43 | throws IOException { | 46 | throws IOException { |
| 44 | boolean isFirst = true; | 47 | boolean isFirst = true; |
| 45 | for (ClassEntry entry : classes) { | 48 | for (ClassEntry entry : classes) { |
| 46 | if (isFirst) { | 49 | if (isFirst) { |
| 47 | isFirst = false; | 50 | isFirst = false; |
| 48 | } else { | 51 | } else { |
| 49 | out.write(","); | 52 | out.write(","); |
| 50 | } | 53 | } |
| 51 | out.write(entry.toString()); | 54 | out.write(entry.toString()); |
| 52 | } | 55 | } |
| 53 | } | 56 | } |
| 54 | 57 | ||
| 55 | public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file) | 58 | public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file) |
| 56 | throws IOException { | 59 | throws IOException { |
| 57 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { | 60 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { |
| 58 | for (Map.Entry<T, T> match : matches.matches().entrySet()) { | 61 | for (Map.Entry<T, T> match : matches.matches().entrySet()) { |
| 59 | writeMemberMatch(out, match.getKey(), match.getValue()); | 62 | writeMemberMatch(out, match.getKey(), match.getValue()); |
| 60 | } | 63 | } |
| 61 | for (T entry : matches.getUnmatchedSourceEntries()) { | 64 | for (T entry : matches.getUnmatchedSourceEntries()) { |
| 62 | writeMemberMatch(out, entry, null); | 65 | writeMemberMatch(out, entry, null); |
| 63 | } | 66 | } |
| 64 | for (T entry : matches.getUnmatchedDestEntries()) { | 67 | for (T entry : matches.getUnmatchedDestEntries()) { |
| 65 | writeMemberMatch(out, null, entry); | 68 | writeMemberMatch(out, null, entry); |
| 66 | } | 69 | } |
| 67 | for (T entry : matches.getUnmatchableSourceEntries()) { | 70 | for (T entry : matches.getUnmatchableSourceEntries()) { |
| 68 | writeUnmatchableEntry(out, entry); | 71 | writeUnmatchableEntry(out, entry); |
| 69 | } | 72 | } |
| 70 | } | 73 | } |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest) | 76 | private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest) |
| 74 | throws IOException { | 77 | throws IOException { |
| 75 | if (source != null) { | 78 | if (source != null) { |
| 76 | writeEntry(out, source); | 79 | writeEntry(out, source); |
| 77 | } | 80 | } |
| 78 | out.write(":"); | 81 | out.write(":"); |
| 79 | if (dest != null) { | 82 | if (dest != null) { |
| 80 | writeEntry(out, dest); | 83 | writeEntry(out, dest); |
| 81 | } | 84 | } |
| 82 | out.write("\n"); | 85 | out.write("\n"); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry) | 88 | private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry) |
| 86 | throws IOException { | 89 | throws IOException { |
| 87 | out.write("!"); | 90 | out.write("!"); |
| 88 | writeEntry(out, entry); | 91 | writeEntry(out, entry); |
| 89 | out.write("\n"); | 92 | out.write("\n"); |
| 90 | } | 93 | } |
| 91 | 94 | ||
| 92 | private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry) | 95 | private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry) |
| 93 | throws IOException { | 96 | throws IOException { |
| 94 | if (entry instanceof FieldEntry) { | 97 | if (entry instanceof FieldEntry) { |
| 95 | writeField(out, (FieldEntry) entry); | 98 | writeField(out, (FieldEntry) entry); |
| 96 | } else if (entry instanceof BehaviorEntry) { | 99 | } else if (entry instanceof BehaviorEntry) { |
| 97 | writeBehavior(out, (BehaviorEntry) entry); | 100 | writeBehavior(out, (BehaviorEntry) entry); |
| 98 | } | 101 | } |
| 99 | } | 102 | } |
| 100 | 103 | ||
| 101 | private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) | 104 | private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) |
| 102 | throws IOException { | 105 | throws IOException { |
| 103 | out.write(fieldEntry.getClassName()); | 106 | out.write(fieldEntry.getClassName()); |
| 104 | out.write(" "); | 107 | out.write(" "); |
| 105 | out.write(fieldEntry.getName()); | 108 | out.write(fieldEntry.getName()); |
| 106 | out.write(" "); | 109 | out.write(" "); |
| 107 | out.write(fieldEntry.getType().toString()); | 110 | out.write(fieldEntry.getType().toString()); |
| 108 | } | 111 | } |
| 109 | 112 | ||
| 110 | private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) | 113 | private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) |
| 111 | throws IOException { | 114 | throws IOException { |
| 112 | out.write(behaviorEntry.getClassName()); | 115 | out.write(behaviorEntry.getClassName()); |
| 113 | out.write(" "); | 116 | out.write(" "); |
| 114 | out.write(behaviorEntry.getName()); | 117 | out.write(behaviorEntry.getName()); |
| 115 | out.write(" "); | 118 | out.write(" "); |
| 116 | if (behaviorEntry.getSignature() != null) { | 119 | if (behaviorEntry.getSignature() != null) { |
| 117 | out.write(behaviorEntry.getSignature().toString()); | 120 | out.write(behaviorEntry.getSignature().toString()); |
| 118 | } | 121 | } |
| 119 | } | 122 | } |
| 120 | } | 123 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MemberMatches.java b/src/main/java/cuchaz/enigma/convert/MemberMatches.java index 51cee859..bd743115 100644 --- a/src/main/java/cuchaz/enigma/convert/MemberMatches.java +++ b/src/main/java/cuchaz/enigma/convert/MemberMatches.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| @@ -18,165 +19,161 @@ import cuchaz.enigma.mapping.Entry; | |||
| 18 | import java.util.Collection; | 19 | import java.util.Collection; |
| 19 | import java.util.Set; | 20 | import java.util.Set; |
| 20 | 21 | ||
| 21 | |||
| 22 | public class MemberMatches<T extends Entry> { | 22 | public class MemberMatches<T extends Entry> { |
| 23 | 23 | ||
| 24 | private BiMap<T, T> matches; | 24 | private BiMap<T, T> matches; |
| 25 | private Multimap<ClassEntry, T> matchedSourceEntries; | 25 | private Multimap<ClassEntry, T> matchedSourceEntries; |
| 26 | private Multimap<ClassEntry, T> unmatchedSourceEntries; | 26 | private Multimap<ClassEntry, T> unmatchedSourceEntries; |
| 27 | private Multimap<ClassEntry, T> unmatchedDestEntries; | 27 | private Multimap<ClassEntry, T> unmatchedDestEntries; |
| 28 | private Multimap<ClassEntry, T> unmatchableSourceEntries; | 28 | private Multimap<ClassEntry, T> unmatchableSourceEntries; |
| 29 | 29 | ||
| 30 | public MemberMatches() { | 30 | public MemberMatches() { |
| 31 | matches = HashBiMap.create(); | 31 | matches = HashBiMap.create(); |
| 32 | matchedSourceEntries = HashMultimap.create(); | 32 | matchedSourceEntries = HashMultimap.create(); |
| 33 | unmatchedSourceEntries = HashMultimap.create(); | 33 | unmatchedSourceEntries = HashMultimap.create(); |
| 34 | unmatchedDestEntries = HashMultimap.create(); | 34 | unmatchedDestEntries = HashMultimap.create(); |
| 35 | unmatchableSourceEntries = HashMultimap.create(); | 35 | unmatchableSourceEntries = HashMultimap.create(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | public void addMatch(T srcEntry, T destEntry) { | 38 | public void addMatch(T srcEntry, T destEntry) { |
| 39 | boolean wasAdded = matches.put(srcEntry, destEntry) == null; | 39 | boolean wasAdded = matches.put(srcEntry, destEntry) == null; |
| 40 | assert (wasAdded); | 40 | assert (wasAdded); |
| 41 | wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); | 41 | wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); |
| 42 | assert (wasAdded); | 42 | assert (wasAdded); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | public void addUnmatchedSourceEntry(T sourceEntry) { | 45 | public void addUnmatchedSourceEntry(T sourceEntry) { |
| 46 | boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); | 46 | boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); |
| 47 | assert (wasAdded); | 47 | assert (wasAdded); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) { | 50 | public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) { |
| 51 | for (T sourceEntry : sourceEntries) { | 51 | for (T sourceEntry : sourceEntries) { |
| 52 | addUnmatchedSourceEntry(sourceEntry); | 52 | addUnmatchedSourceEntry(sourceEntry); |
| 53 | } | 53 | } |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | public void addUnmatchedDestEntry(T destEntry) { | 56 | public void addUnmatchedDestEntry(T destEntry) { |
| 57 | if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>")) | 57 | if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>")) |
| 58 | return; | 58 | return; |
| 59 | boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); | 59 | boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); |
| 60 | assert (wasAdded); | 60 | assert (wasAdded); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) { | 63 | public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) { |
| 64 | for (T entry : destEntriesntries) { | 64 | for (T entry : destEntriesntries) { |
| 65 | addUnmatchedDestEntry(entry); | 65 | addUnmatchedDestEntry(entry); |
| 66 | } | 66 | } |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | public void addUnmatchableSourceEntry(T sourceEntry) { | 69 | public void addUnmatchableSourceEntry(T sourceEntry) { |
| 70 | boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); | 70 | boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); |
| 71 | assert (wasAdded); | 71 | assert (wasAdded); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() { | 74 | public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() { |
| 75 | return unmatchedSourceEntries.keySet(); | 75 | return unmatchedSourceEntries.keySet(); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() { | 78 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() { |
| 79 | Set<ClassEntry> out = Sets.newHashSet(); | 79 | Set<ClassEntry> out = Sets.newHashSet(); |
| 80 | out.addAll(matchedSourceEntries.keySet()); | 80 | out.addAll(matchedSourceEntries.keySet()); |
| 81 | out.removeAll(unmatchedSourceEntries.keySet()); | 81 | out.removeAll(unmatchedSourceEntries.keySet()); |
| 82 | return out; | 82 | return out; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | public Collection<T> getUnmatchedSourceEntries() { | 85 | public Collection<T> getUnmatchedSourceEntries() { |
| 86 | return unmatchedSourceEntries.values(); | 86 | return unmatchedSourceEntries.values(); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) { | 89 | public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) { |
| 90 | return unmatchedSourceEntries.get(sourceClass); | 90 | return unmatchedSourceEntries.get(sourceClass); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | public Collection<T> getUnmatchedDestEntries() { | 93 | public Collection<T> getUnmatchedDestEntries() { |
| 94 | return unmatchedDestEntries.values(); | 94 | return unmatchedDestEntries.values(); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) { | 97 | public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) { |
| 98 | return unmatchedDestEntries.get(destClass); | 98 | return unmatchedDestEntries.get(destClass); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | public Collection<T> getUnmatchableSourceEntries() { | 101 | public Collection<T> getUnmatchableSourceEntries() { |
| 102 | return unmatchableSourceEntries.values(); | 102 | return unmatchableSourceEntries.values(); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | public boolean hasSource(T sourceEntry) { | 105 | public boolean hasSource(T sourceEntry) { |
| 106 | return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); | 106 | return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | public boolean hasDest(T destEntry) { | 109 | public boolean hasDest(T destEntry) { |
| 110 | return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); | 110 | return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | public BiMap<T, T> matches() { | 113 | public BiMap<T, T> matches() { |
| 114 | return matches; | 114 | return matches; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | public boolean isMatchedSourceEntry(T sourceEntry) { | 117 | public boolean isMatchedSourceEntry(T sourceEntry) { |
| 118 | return matches.containsKey(sourceEntry); | 118 | return matches.containsKey(sourceEntry); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | public boolean isMatchedDestEntry(T destEntry) { | 121 | public boolean isMatchedDestEntry(T destEntry) { |
| 122 | return matches.containsValue(destEntry); | 122 | return matches.containsValue(destEntry); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | public boolean isUnmatchableSourceEntry(T sourceEntry) { | 125 | public boolean isUnmatchableSourceEntry(T sourceEntry) { |
| 126 | return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); | 126 | return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); |
| 127 | } | 127 | } |
| 128 | public void makeMatch(T sourceEntry, T destEntry) { | 128 | |
| 129 | makeMatch(sourceEntry, destEntry, null, null); | 129 | public void makeMatch(T sourceEntry, T destEntry) { |
| 130 | } | 130 | makeMatch(sourceEntry, destEntry, null, null); |
| 131 | 131 | } | |
| 132 | public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 132 | |
| 133 | if (sourceDeobfuscator != null && destDeobfuscator != null) | 133 | public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 134 | { | 134 | if (sourceDeobfuscator != null && destDeobfuscator != null) { |
| 135 | makeMatch(sourceEntry, destEntry); | 135 | makeMatch(sourceEntry, destEntry); |
| 136 | sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 136 | sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 137 | destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); | 137 | destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); |
| 138 | } | 138 | } |
| 139 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 139 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 140 | assert (wasRemoved); | 140 | assert (wasRemoved); |
| 141 | wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); | 141 | wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); |
| 142 | assert (wasRemoved); | 142 | assert (wasRemoved); |
| 143 | addMatch(sourceEntry, destEntry); | 143 | addMatch(sourceEntry, destEntry); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | public boolean isMatched(T sourceEntry, T destEntry) { | 146 | public boolean isMatched(T sourceEntry, T destEntry) { |
| 147 | T match = matches.get(sourceEntry); | 147 | T match = matches.get(sourceEntry); |
| 148 | return match != null && match.equals(destEntry); | 148 | return match != null && match.equals(destEntry); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) | 151 | public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 152 | { | 152 | if (sourceDeobfuscator != null && destDeobfuscator != null) { |
| 153 | if (sourceDeobfuscator != null && destDeobfuscator != null) | 153 | unmakeMatch(sourceEntry, destEntry, null, null); |
| 154 | { | 154 | sourceEntry = (T) sourceEntry.cloneToNewClass( |
| 155 | unmakeMatch(sourceEntry, destEntry, null, null); | 155 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 156 | sourceEntry = (T) sourceEntry.cloneToNewClass( | 156 | destEntry = (T) destEntry.cloneToNewClass( |
| 157 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 157 | destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); |
| 158 | destEntry = (T) destEntry.cloneToNewClass( | 158 | } |
| 159 | destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); | 159 | |
| 160 | } | 160 | boolean wasRemoved = matches.remove(sourceEntry) != null; |
| 161 | 161 | assert (wasRemoved); | |
| 162 | boolean wasRemoved = matches.remove(sourceEntry) != null; | 162 | wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 163 | assert (wasRemoved); | 163 | assert (wasRemoved); |
| 164 | wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 164 | addUnmatchedSourceEntry(sourceEntry); |
| 165 | assert (wasRemoved); | 165 | addUnmatchedDestEntry(destEntry); |
| 166 | addUnmatchedSourceEntry(sourceEntry); | 166 | } |
| 167 | addUnmatchedDestEntry(destEntry); | 167 | |
| 168 | } | 168 | public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { |
| 169 | 169 | if (sourceDeobfuscator != null) { | |
| 170 | public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { | 170 | makeSourceUnmatchable(sourceEntry, null); |
| 171 | if (sourceDeobfuscator != null) | 171 | sourceEntry = (T) sourceEntry.cloneToNewClass( |
| 172 | { | 172 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 173 | makeSourceUnmatchable(sourceEntry, null); | 173 | } |
| 174 | sourceEntry = (T) sourceEntry.cloneToNewClass( | 174 | assert (!isMatchedSourceEntry(sourceEntry)); |
| 175 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 175 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 176 | } | 176 | assert (wasRemoved); |
| 177 | assert (!isMatchedSourceEntry(sourceEntry)); | 177 | addUnmatchableSourceEntry(sourceEntry); |
| 178 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 178 | } |
| 179 | assert (wasRemoved); | ||
| 180 | addUnmatchableSourceEntry(sourceEntry); | ||
| 181 | } | ||
| 182 | } | 179 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java index bcdff51e..af105dbd 100644 --- a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java +++ b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java | |||
| @@ -8,20 +8,21 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import javax.swing.text.DefaultCaret; | 14 | import javax.swing.text.DefaultCaret; |
| 14 | 15 | ||
| 15 | public class BrowserCaret extends DefaultCaret { | 16 | public class BrowserCaret extends DefaultCaret { |
| 16 | 17 | ||
| 17 | @Override | 18 | @Override |
| 18 | public boolean isSelectionVisible() { | 19 | public boolean isSelectionVisible() { |
| 19 | return true; | 20 | return true; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | @Override | 23 | @Override |
| 23 | public boolean isVisible() { | 24 | public boolean isVisible() { |
| 24 | return true; | 25 | return true; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | } | 28 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java index dcbe1c5b..05501f40 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| @@ -31,508 +32,505 @@ import java.util.Collection; | |||
| 31 | import java.util.List; | 32 | import java.util.List; |
| 32 | import java.util.Map; | 33 | import java.util.Map; |
| 33 | 34 | ||
| 34 | |||
| 35 | public class ClassMatchingGui { | 35 | public class ClassMatchingGui { |
| 36 | 36 | ||
| 37 | private enum SourceType { | 37 | // controls |
| 38 | Matched { | 38 | private JFrame frame; |
| 39 | @Override | 39 | private ClassSelector sourceClasses; |
| 40 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 40 | private ClassSelector destClasses; |
| 41 | return matches.getUniqueMatches().keySet(); | 41 | private CodeReader sourceReader; |
| 42 | } | 42 | private CodeReader destReader; |
| 43 | }, | 43 | private JLabel sourceClassLabel; |
| 44 | Unmatched { | 44 | private JLabel destClassLabel; |
| 45 | @Override | 45 | private JButton matchButton; |
| 46 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 46 | private Map<SourceType, JRadioButton> sourceTypeButtons; |
| 47 | return matches.getUnmatchedSourceClasses(); | 47 | private JCheckBox advanceCheck; |
| 48 | } | 48 | private JCheckBox top10Matches; |
| 49 | }, | 49 | private ClassMatches classMatches; |
| 50 | Ambiguous { | 50 | private Deobfuscator sourceDeobfuscator; |
| 51 | @Override | 51 | private Deobfuscator destDeobfuscator; |
| 52 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | 52 | private ClassEntry sourceClass; |
| 53 | return matches.getAmbiguouslyMatchedSourceClasses(); | 53 | private ClassEntry destClass; |
| 54 | } | 54 | private SourceType sourceType; |
| 55 | }; | 55 | private SaveListener saveListener; |
| 56 | 56 | public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | |
| 57 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | 57 | |
| 58 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | 58 | classMatches = matches; |
| 59 | button.setActionCommand(name()); | 59 | this.sourceDeobfuscator = sourceDeobfuscator; |
| 60 | button.addActionListener(listener); | 60 | this.destDeobfuscator = destDeobfuscator; |
| 61 | group.add(button); | 61 | |
| 62 | return button; | 62 | // init frame |
| 63 | } | 63 | frame = new JFrame(Constants.NAME + " - Class Matcher"); |
| 64 | 64 | final Container pane = frame.getContentPane(); | |
| 65 | public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches); | 65 | pane.setLayout(new BorderLayout()); |
| 66 | 66 | ||
| 67 | public static SourceType getDefault() { | 67 | // init source side |
| 68 | return values()[0]; | 68 | JPanel sourcePanel = new JPanel(); |
| 69 | } | 69 | sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); |
| 70 | } | 70 | sourcePanel.setPreferredSize(new Dimension(200, 0)); |
| 71 | 71 | pane.add(sourcePanel, BorderLayout.WEST); | |
| 72 | public interface SaveListener { | 72 | sourcePanel.add(new JLabel("Source Classes")); |
| 73 | void save(ClassMatches matches); | 73 | |
| 74 | } | 74 | // init source type radios |
| 75 | 75 | JPanel sourceTypePanel = new JPanel(); | |
| 76 | // controls | 76 | sourcePanel.add(sourceTypePanel); |
| 77 | private JFrame frame; | 77 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); |
| 78 | private ClassSelector sourceClasses; | 78 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); |
| 79 | private ClassSelector destClasses; | 79 | ButtonGroup sourceTypeButtons = new ButtonGroup(); |
| 80 | private CodeReader sourceReader; | 80 | this.sourceTypeButtons = Maps.newHashMap(); |
| 81 | private CodeReader destReader; | 81 | for (SourceType sourceType : SourceType.values()) { |
| 82 | private JLabel sourceClassLabel; | 82 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); |
| 83 | private JLabel destClassLabel; | 83 | this.sourceTypeButtons.put(sourceType, button); |
| 84 | private JButton matchButton; | 84 | sourceTypePanel.add(button); |
| 85 | private Map<SourceType, JRadioButton> sourceTypeButtons; | 85 | } |
| 86 | private JCheckBox advanceCheck; | 86 | |
| 87 | private JCheckBox top10Matches; | 87 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 88 | 88 | sourceClasses.setSelectionListener(this::setSourceClass); | |
| 89 | private ClassMatches classMatches; | 89 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); |
| 90 | private Deobfuscator sourceDeobfuscator; | 90 | sourcePanel.add(sourceScroller); |
| 91 | private Deobfuscator destDeobfuscator; | 91 | |
| 92 | private ClassEntry sourceClass; | 92 | // init dest side |
| 93 | private ClassEntry destClass; | 93 | JPanel destPanel = new JPanel(); |
| 94 | private SourceType sourceType; | 94 | destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); |
| 95 | private SaveListener saveListener; | 95 | destPanel.setPreferredSize(new Dimension(200, 0)); |
| 96 | 96 | pane.add(destPanel, BorderLayout.WEST); | |
| 97 | public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 97 | destPanel.add(new JLabel("Destination Classes")); |
| 98 | 98 | ||
| 99 | classMatches = matches; | 99 | top10Matches = new JCheckBox("Show only top 10 matches"); |
| 100 | this.sourceDeobfuscator = sourceDeobfuscator; | 100 | destPanel.add(top10Matches); |
| 101 | this.destDeobfuscator = destDeobfuscator; | 101 | top10Matches.addActionListener(event -> toggleTop10Matches()); |
| 102 | 102 | ||
| 103 | // init frame | 103 | destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 104 | frame = new JFrame(Constants.NAME + " - Class Matcher"); | 104 | destClasses.setSelectionListener(this::setDestClass); |
| 105 | final Container pane = frame.getContentPane(); | 105 | JScrollPane destScroller = new JScrollPane(destClasses); |
| 106 | pane.setLayout(new BorderLayout()); | 106 | destPanel.add(destScroller); |
| 107 | 107 | ||
| 108 | // init source side | 108 | JButton autoMatchButton = new JButton("AutoMatch"); |
| 109 | JPanel sourcePanel = new JPanel(); | 109 | autoMatchButton.addActionListener(event -> autoMatch()); |
| 110 | sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); | 110 | destPanel.add(autoMatchButton); |
| 111 | sourcePanel.setPreferredSize(new Dimension(200, 0)); | 111 | |
| 112 | pane.add(sourcePanel, BorderLayout.WEST); | 112 | // init source panels |
| 113 | sourcePanel.add(new JLabel("Source Classes")); | 113 | DefaultSyntaxKit.initKit(); |
| 114 | 114 | sourceReader = new CodeReader(); | |
| 115 | // init source type radios | 115 | destReader = new CodeReader(); |
| 116 | JPanel sourceTypePanel = new JPanel(); | 116 | |
| 117 | sourcePanel.add(sourceTypePanel); | 117 | // init all the splits |
| 118 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); | 118 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( |
| 119 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); | 119 | sourceReader)); |
| 120 | ButtonGroup sourceTypeButtons = new ButtonGroup(); | 120 | splitLeft.setResizeWeight(0); // let the right side take all the slack |
| 121 | this.sourceTypeButtons = Maps.newHashMap(); | 121 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); |
| 122 | for (SourceType sourceType : SourceType.values()) { | 122 | splitRight.setResizeWeight(1); // let the left side take all the slack |
| 123 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); | 123 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); |
| 124 | this.sourceTypeButtons.put(sourceType, button); | 124 | splitCenter.setResizeWeight(0.5); // resize 50:50 |
| 125 | sourceTypePanel.add(button); | 125 | pane.add(splitCenter, BorderLayout.CENTER); |
| 126 | } | 126 | splitCenter.resetToPreferredSizes(); |
| 127 | 127 | ||
| 128 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 128 | // init bottom panel |
| 129 | sourceClasses.setSelectionListener(this::setSourceClass); | 129 | JPanel bottomPanel = new JPanel(); |
| 130 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); | 130 | bottomPanel.setLayout(new FlowLayout()); |
| 131 | sourcePanel.add(sourceScroller); | 131 | |
| 132 | 132 | sourceClassLabel = new JLabel(); | |
| 133 | // init dest side | 133 | sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| 134 | JPanel destPanel = new JPanel(); | 134 | destClassLabel = new JLabel(); |
| 135 | destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); | 135 | destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); |
| 136 | destPanel.setPreferredSize(new Dimension(200, 0)); | 136 | |
| 137 | pane.add(destPanel, BorderLayout.WEST); | 137 | matchButton = new JButton(); |
| 138 | destPanel.add(new JLabel("Destination Classes")); | 138 | |
| 139 | 139 | advanceCheck = new JCheckBox("Advance to next likely match"); | |
| 140 | top10Matches = new JCheckBox("Show only top 10 matches"); | 140 | advanceCheck.addActionListener(event -> { |
| 141 | destPanel.add(top10Matches); | 141 | if (advanceCheck.isSelected()) { |
| 142 | top10Matches.addActionListener(event -> toggleTop10Matches()); | 142 | advance(); |
| 143 | 143 | } | |
| 144 | destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 144 | }); |
| 145 | destClasses.setSelectionListener(this::setDestClass); | 145 | |
| 146 | JScrollPane destScroller = new JScrollPane(destClasses); | 146 | bottomPanel.add(sourceClassLabel); |
| 147 | destPanel.add(destScroller); | 147 | bottomPanel.add(matchButton); |
| 148 | 148 | bottomPanel.add(destClassLabel); | |
| 149 | JButton autoMatchButton = new JButton("AutoMatch"); | 149 | bottomPanel.add(advanceCheck); |
| 150 | autoMatchButton.addActionListener(event -> autoMatch()); | 150 | pane.add(bottomPanel, BorderLayout.SOUTH); |
| 151 | destPanel.add(autoMatchButton); | 151 | |
| 152 | 152 | // show the frame | |
| 153 | // init source panels | 153 | pane.doLayout(); |
| 154 | DefaultSyntaxKit.initKit(); | 154 | frame.setSize(1024, 576); |
| 155 | sourceReader = new CodeReader(); | 155 | frame.setMinimumSize(new Dimension(640, 480)); |
| 156 | destReader = new CodeReader(); | 156 | frame.setVisible(true); |
| 157 | 157 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | |
| 158 | // init all the splits | 158 | |
| 159 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane( | 159 | // init state |
| 160 | sourceReader)); | 160 | updateDestMappings(); |
| 161 | splitLeft.setResizeWeight(0); // let the right side take all the slack | 161 | setSourceType(SourceType.getDefault()); |
| 162 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(destReader), destPanel); | 162 | updateMatchButton(); |
| 163 | splitRight.setResizeWeight(1); // let the left side take all the slack | 163 | saveListener = null; |
| 164 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); | 164 | } |
| 165 | splitCenter.setResizeWeight(0.5); // resize 50:50 | 165 | |
| 166 | pane.add(splitCenter, BorderLayout.CENTER); | 166 | public void setSaveListener(SaveListener val) { |
| 167 | splitCenter.resetToPreferredSizes(); | 167 | saveListener = val; |
| 168 | 168 | } | |
| 169 | // init bottom panel | 169 | |
| 170 | JPanel bottomPanel = new JPanel(); | 170 | private void updateDestMappings() { |
| 171 | bottomPanel.setLayout(new FlowLayout()); | 171 | try { |
| 172 | 172 | Mappings newMappings = MappingsConverter.newMappings(classMatches, | |
| 173 | sourceClassLabel = new JLabel(); | 173 | sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator |
| 174 | sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); | 174 | ); |
| 175 | destClassLabel = new JLabel(); | 175 | |
| 176 | destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); | 176 | // look for dropped mappings |
| 177 | 177 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | |
| 178 | matchButton = new JButton(); | 178 | checker.dropBrokenMappings(newMappings); |
| 179 | 179 | ||
| 180 | advanceCheck = new JCheckBox("Advance to next likely match"); | 180 | // count them |
| 181 | advanceCheck.addActionListener(event -> { | 181 | int numDroppedFields = checker.getDroppedFieldMappings().size(); |
| 182 | if (advanceCheck.isSelected()) { | 182 | int numDroppedMethods = checker.getDroppedMethodMappings().size(); |
| 183 | advance(); | 183 | System.out.println(String.format( |
| 184 | } | 184 | "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", |
| 185 | }); | 185 | numDroppedFields + numDroppedMethods, |
| 186 | 186 | numDroppedFields, | |
| 187 | bottomPanel.add(sourceClassLabel); | 187 | numDroppedMethods |
| 188 | bottomPanel.add(matchButton); | 188 | )); |
| 189 | bottomPanel.add(destClassLabel); | 189 | |
| 190 | bottomPanel.add(advanceCheck); | 190 | destDeobfuscator.setMappings(newMappings); |
| 191 | pane.add(bottomPanel, BorderLayout.SOUTH); | 191 | } catch (MappingConflict ex) { |
| 192 | 192 | System.out.println(ex.getMessage()); | |
| 193 | // show the frame | 193 | ex.printStackTrace(); |
| 194 | pane.doLayout(); | 194 | return; |
| 195 | frame.setSize(1024, 576); | 195 | } |
| 196 | frame.setMinimumSize(new Dimension(640, 480)); | 196 | } |
| 197 | frame.setVisible(true); | 197 | |
| 198 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 198 | protected void setSourceType(SourceType val) { |
| 199 | 199 | ||
| 200 | // init state | 200 | // show the source classes |
| 201 | updateDestMappings(); | 201 | sourceType = val; |
| 202 | setSourceType(SourceType.getDefault()); | 202 | sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); |
| 203 | updateMatchButton(); | 203 | |
| 204 | saveListener = null; | 204 | // update counts |
| 205 | } | 205 | for (SourceType sourceType : SourceType.values()) { |
| 206 | 206 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | |
| 207 | public void setSaveListener(SaveListener val) { | 207 | sourceType.name(), |
| 208 | saveListener = val; | 208 | sourceType.getSourceClasses(classMatches).size() |
| 209 | } | 209 | )); |
| 210 | 210 | } | |
| 211 | private void updateDestMappings() { | 211 | } |
| 212 | try { | 212 | |
| 213 | Mappings newMappings = MappingsConverter.newMappings(classMatches, | 213 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { |
| 214 | sourceDeobfuscator.getMappings(), sourceDeobfuscator, destDeobfuscator | 214 | List<ClassEntry> out = Lists.newArrayList(); |
| 215 | ); | 215 | for (ClassEntry entry : in) { |
| 216 | 216 | ||
| 217 | // look for dropped mappings | 217 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); |
| 218 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 218 | |
| 219 | checker.dropBrokenMappings(newMappings); | 219 | // make sure we preserve any scores |
| 220 | 220 | if (entry instanceof ScoredClassEntry) { | |
| 221 | // count them | 221 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); |
| 222 | int numDroppedFields = checker.getDroppedFieldMappings().size(); | 222 | } |
| 223 | int numDroppedMethods = checker.getDroppedMethodMappings().size(); | 223 | |
| 224 | System.out.println(String.format( | 224 | out.add(deobf); |
| 225 | "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods", | 225 | } |
| 226 | numDroppedFields + numDroppedMethods, | 226 | return out; |
| 227 | numDroppedFields, | 227 | } |
| 228 | numDroppedMethods | 228 | |
| 229 | )); | 229 | protected void setSourceClass(ClassEntry classEntry) { |
| 230 | 230 | ||
| 231 | destDeobfuscator.setMappings(newMappings); | 231 | Runnable onGetDestClasses = null; |
| 232 | } catch (MappingConflict ex) { | 232 | if (advanceCheck.isSelected()) { |
| 233 | System.out.println(ex.getMessage()); | 233 | onGetDestClasses = this::pickBestDestClass; |
| 234 | ex.printStackTrace(); | 234 | } |
| 235 | return; | 235 | |
| 236 | } | 236 | setSourceClass(classEntry, onGetDestClasses); |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | protected void setSourceType(SourceType val) { | 239 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { |
| 240 | 240 | ||
| 241 | // show the source classes | 241 | // update the current source class |
| 242 | sourceType = val; | 242 | sourceClass = classEntry; |
| 243 | sourceClasses.setClasses(deobfuscateClasses(sourceType.getSourceClasses(classMatches), sourceDeobfuscator)); | 243 | sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); |
| 244 | 244 | ||
| 245 | // update counts | 245 | if (sourceClass != null) { |
| 246 | for (SourceType sourceType : SourceType.values()) { | 246 | |
| 247 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | 247 | // show the dest class(es) |
| 248 | sourceType.name(), | 248 | ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); |
| 249 | sourceType.getSourceClasses(classMatches).size() | 249 | assert (match != null); |
| 250 | )); | 250 | if (match.destClasses.isEmpty()) { |
| 251 | } | 251 | |
| 252 | } | 252 | destClasses.setClasses(null); |
| 253 | 253 | ||
| 254 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { | 254 | // run in a separate thread to keep ui responsive |
| 255 | List<ClassEntry> out = Lists.newArrayList(); | 255 | new Thread(() -> |
| 256 | for (ClassEntry entry : in) { | 256 | { |
| 257 | 257 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | |
| 258 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); | 258 | destClasses.expandAll(); |
| 259 | 259 | ||
| 260 | // make sure we preserve any scores | 260 | if (onGetDestClasses != null) { |
| 261 | if (entry instanceof ScoredClassEntry) { | 261 | onGetDestClasses.run(); |
| 262 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore()); | 262 | } |
| 263 | } | 263 | }).start(); |
| 264 | 264 | ||
| 265 | out.add(deobf); | 265 | } else { |
| 266 | } | 266 | |
| 267 | return out; | 267 | destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator)); |
| 268 | } | 268 | destClasses.expandAll(); |
| 269 | 269 | ||
| 270 | protected void setSourceClass(ClassEntry classEntry) { | 270 | if (onGetDestClasses != null) { |
| 271 | 271 | onGetDestClasses.run(); | |
| 272 | Runnable onGetDestClasses = null; | 272 | } |
| 273 | if (advanceCheck.isSelected()) { | 273 | } |
| 274 | onGetDestClasses = this::pickBestDestClass; | 274 | } |
| 275 | } | 275 | |
| 276 | 276 | setDestClass(null); | |
| 277 | setSourceClass(classEntry, onGetDestClasses); | 277 | sourceReader.decompileClass( |
| 278 | } | 278 | sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass)); |
| 279 | 279 | ||
| 280 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { | 280 | updateMatchButton(); |
| 281 | 281 | } | |
| 282 | // update the current source class | 282 | |
| 283 | sourceClass = classEntry; | 283 | private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) { |
| 284 | sourceClassLabel.setText(sourceClass != null ? sourceClass.getName() : ""); | 284 | |
| 285 | 285 | ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | |
| 286 | if (sourceClass != null) { | 286 | |
| 287 | 287 | // set up identifiers | |
| 288 | // show the dest class(es) | 288 | ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches()); |
| 289 | ClassMatch match = classMatches.getMatchBySource(sourceDeobfuscator.obfuscateEntry(sourceClass)); | 289 | ClassIdentifier sourceIdentifier = new ClassIdentifier( |
| 290 | assert (match != null); | 290 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), |
| 291 | if (match.destClasses.isEmpty()) { | 291 | namer.getSourceNamer(), true |
| 292 | 292 | ); | |
| 293 | destClasses.setClasses(null); | 293 | ClassIdentifier destIdentifier = new ClassIdentifier( |
| 294 | 294 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | |
| 295 | // run in a separate thread to keep ui responsive | 295 | namer.getDestNamer(), true |
| 296 | new Thread(() -> | 296 | ); |
| 297 | { | ||
| 298 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | ||
| 299 | destClasses.expandAll(); | ||
| 300 | |||
| 301 | if (onGetDestClasses != null) { | ||
| 302 | onGetDestClasses.run(); | ||
| 303 | } | ||
| 304 | }).start(); | ||
| 305 | |||
| 306 | } else { | ||
| 307 | |||
| 308 | destClasses.setClasses(deobfuscateClasses(match.destClasses, destDeobfuscator)); | ||
| 309 | destClasses.expandAll(); | ||
| 310 | |||
| 311 | if (onGetDestClasses != null) { | ||
| 312 | onGetDestClasses.run(); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | setDestClass(null); | ||
| 318 | sourceReader.decompileClass( | ||
| 319 | sourceClass, sourceDeobfuscator, () -> sourceReader.navigateToClassDeclaration(sourceClass)); | ||
| 320 | |||
| 321 | updateMatchButton(); | ||
| 322 | } | ||
| 323 | |||
| 324 | private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) { | ||
| 325 | |||
| 326 | ClassEntry obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | ||
| 327 | |||
| 328 | // set up identifiers | ||
| 329 | ClassNamer namer = new ClassNamer(classMatches.getUniqueMatches()); | ||
| 330 | ClassIdentifier sourceIdentifier = new ClassIdentifier( | ||
| 331 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), | ||
| 332 | namer.getSourceNamer(), true | ||
| 333 | ); | ||
| 334 | ClassIdentifier destIdentifier = new ClassIdentifier( | ||
| 335 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | ||
| 336 | namer.getDestNamer(), true | ||
| 337 | ); | ||
| 338 | 297 | ||
| 339 | try { | 298 | try { |
| 340 | 299 | ||
| 341 | // rank all the unmatched dest classes against the source class | 300 | // rank all the unmatched dest classes against the source class |
| 342 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); | 301 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); |
| 343 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); | 302 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); |
| 344 | for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { | 303 | for (ClassEntry unmatchedDestClass : classMatches.getUnmatchedDestClasses()) { |
| 345 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); | 304 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); |
| 346 | float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) | 305 | float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) |
| 347 | / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); | 306 | / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); |
| 348 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); | 307 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); |
| 349 | } | 308 | } |
| 350 | 309 | ||
| 351 | if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { | 310 | if (top10Matches.isSelected() && scoredDestClasses.size() > 10) { |
| 352 | scoredDestClasses.sort((a, b) -> | 311 | scoredDestClasses.sort((a, b) -> |
| 353 | { | 312 | { |
| 354 | ScoredClassEntry sa = (ScoredClassEntry) a; | 313 | ScoredClassEntry sa = (ScoredClassEntry) a; |
| 355 | ScoredClassEntry sb = (ScoredClassEntry) b; | 314 | ScoredClassEntry sb = (ScoredClassEntry) b; |
| 356 | return -Float.compare(sa.getScore(), sb.getScore()); | 315 | return -Float.compare(sa.getScore(), sb.getScore()); |
| 357 | }); | 316 | }); |
| 358 | scoredDestClasses = scoredDestClasses.subList(0, 10); | 317 | scoredDestClasses = scoredDestClasses.subList(0, 10); |
| 359 | } | 318 | } |
| 360 | 319 | ||
| 361 | return scoredDestClasses; | 320 | return scoredDestClasses; |
| 362 | 321 | ||
| 363 | } catch (ClassNotFoundException ex) { | 322 | } catch (ClassNotFoundException ex) { |
| 364 | throw new Error("Unable to find class " + ex.getMessage()); | 323 | throw new Error("Unable to find class " + ex.getMessage()); |
| 365 | } | 324 | } |
| 366 | } | 325 | } |
| 367 | 326 | ||
| 368 | protected void setDestClass(ClassEntry classEntry) { | 327 | protected void setDestClass(ClassEntry classEntry) { |
| 369 | 328 | ||
| 370 | // update the current source class | 329 | // update the current source class |
| 371 | destClass = classEntry; | 330 | destClass = classEntry; |
| 372 | destClassLabel.setText(destClass != null ? destClass.getName() : ""); | 331 | destClassLabel.setText(destClass != null ? destClass.getName() : ""); |
| 373 | 332 | ||
| 374 | destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); | 333 | destReader.decompileClass(destClass, destDeobfuscator, () -> destReader.navigateToClassDeclaration(destClass)); |
| 375 | 334 | ||
| 376 | updateMatchButton(); | 335 | updateMatchButton(); |
| 377 | } | 336 | } |
| 378 | 337 | ||
| 379 | private void updateMatchButton() { | 338 | private void updateMatchButton() { |
| 380 | 339 | ||
| 381 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 340 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 382 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); | 341 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); |
| 383 | 342 | ||
| 384 | BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches(); | 343 | BiMap<ClassEntry, ClassEntry> uniqueMatches = classMatches.getUniqueMatches(); |
| 385 | boolean twoSelected = sourceClass != null && destClass != null; | 344 | boolean twoSelected = sourceClass != null && destClass != null; |
| 386 | boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); | 345 | boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); |
| 387 | boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); | 346 | boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest); |
| 388 | 347 | ||
| 389 | GuiTricks.deactivateButton(matchButton); | 348 | GuiTricks.deactivateButton(matchButton); |
| 390 | if (twoSelected) { | 349 | if (twoSelected) { |
| 391 | if (isMatched) { | 350 | if (isMatched) { |
| 392 | GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); | 351 | GuiTricks.activateButton(matchButton, "Unmatch", event -> onUnmatchClick()); |
| 393 | } else if (canMatch) { | 352 | } else if (canMatch) { |
| 394 | GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); | 353 | GuiTricks.activateButton(matchButton, "Match", event -> onMatchClick()); |
| 395 | } | 354 | } |
| 396 | } | 355 | } |
| 397 | } | 356 | } |
| 398 | 357 | ||
| 399 | private void onMatchClick() { | 358 | private void onMatchClick() { |
| 400 | // precondition: source and dest classes are set correctly | 359 | // precondition: source and dest classes are set correctly |
| 401 | 360 | ||
| 402 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 361 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 403 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); | 362 | ClassEntry obfDest = destDeobfuscator.obfuscateEntry(destClass); |
| 404 | 363 | ||
| 405 | // remove the classes from their match | 364 | // remove the classes from their match |
| 406 | classMatches.removeSource(obfSource); | 365 | classMatches.removeSource(obfSource); |
| 407 | classMatches.removeDest(obfDest); | 366 | classMatches.removeDest(obfDest); |
| 408 | 367 | ||
| 409 | // add them as matched classes | 368 | // add them as matched classes |
| 410 | classMatches.add(new ClassMatch(obfSource, obfDest)); | 369 | classMatches.add(new ClassMatch(obfSource, obfDest)); |
| 411 | 370 | ||
| 412 | ClassEntry nextClass = null; | 371 | ClassEntry nextClass = null; |
| 413 | if (advanceCheck.isSelected()) { | 372 | if (advanceCheck.isSelected()) { |
| 414 | nextClass = sourceClasses.getNextClass(sourceClass); | 373 | nextClass = sourceClasses.getNextClass(sourceClass); |
| 415 | } | 374 | } |
| 416 | 375 | ||
| 417 | save(); | 376 | save(); |
| 418 | updateMatches(); | 377 | updateMatches(); |
| 419 | 378 | ||
| 420 | if (nextClass != null) { | 379 | if (nextClass != null) { |
| 421 | advance(nextClass); | 380 | advance(nextClass); |
| 422 | } | 381 | } |
| 423 | } | 382 | } |
| 424 | 383 | ||
| 425 | private void onUnmatchClick() { | 384 | private void onUnmatchClick() { |
| 426 | // precondition: source and dest classes are set to a unique match | 385 | // precondition: source and dest classes are set to a unique match |
| 427 | 386 | ||
| 428 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); | 387 | ClassEntry obfSource = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 429 | 388 | ||
| 430 | // remove the source to break the match, then add the source back as unmatched | 389 | // remove the source to break the match, then add the source back as unmatched |
| 431 | classMatches.removeSource(obfSource); | 390 | classMatches.removeSource(obfSource); |
| 432 | classMatches.add(new ClassMatch(obfSource, null)); | 391 | classMatches.add(new ClassMatch(obfSource, null)); |
| 433 | 392 | ||
| 434 | save(); | 393 | save(); |
| 435 | updateMatches(); | 394 | updateMatches(); |
| 436 | } | 395 | } |
| 437 | 396 | ||
| 438 | private void updateMatches() { | 397 | private void updateMatches() { |
| 439 | updateDestMappings(); | 398 | updateDestMappings(); |
| 440 | setDestClass(null); | 399 | setDestClass(null); |
| 441 | destClasses.setClasses(null); | 400 | destClasses.setClasses(null); |
| 442 | updateMatchButton(); | 401 | updateMatchButton(); |
| 443 | 402 | ||
| 444 | // remember where we were in the source tree | 403 | // remember where we were in the source tree |
| 445 | String packageName = sourceClasses.getSelectedPackage(); | 404 | String packageName = sourceClasses.getSelectedPackage(); |
| 446 | 405 | ||
| 447 | setSourceType(sourceType); | 406 | setSourceType(sourceType); |
| 448 | 407 | ||
| 449 | sourceClasses.expandPackage(packageName); | 408 | sourceClasses.expandPackage(packageName); |
| 450 | } | 409 | } |
| 451 | 410 | ||
| 452 | private void save() { | 411 | private void save() { |
| 453 | if (saveListener != null) { | 412 | if (saveListener != null) { |
| 454 | saveListener.save(classMatches); | 413 | saveListener.save(classMatches); |
| 455 | } | 414 | } |
| 456 | } | 415 | } |
| 457 | 416 | ||
| 458 | private void autoMatch() { | 417 | private void autoMatch() { |
| 459 | 418 | ||
| 460 | System.out.println("Automatching..."); | 419 | System.out.println("Automatching..."); |
| 461 | 420 | ||
| 462 | // compute a new matching | 421 | // compute a new matching |
| 463 | ClassMatching matching = MappingsConverter.computeMatching( | 422 | ClassMatching matching = MappingsConverter.computeMatching( |
| 464 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), | 423 | sourceDeobfuscator.getJar(), sourceDeobfuscator.getJarIndex(), |
| 465 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), | 424 | destDeobfuscator.getJar(), destDeobfuscator.getJarIndex(), |
| 466 | classMatches.getUniqueMatches() | 425 | classMatches.getUniqueMatches() |
| 467 | ); | 426 | ); |
| 468 | ClassMatches newMatches = new ClassMatches(matching.matches()); | 427 | ClassMatches newMatches = new ClassMatches(matching.matches()); |
| 469 | System.out.println(String.format("Automatch found %d new matches", | 428 | System.out.println(String.format("Automatch found %d new matches", |
| 470 | newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() | 429 | newMatches.getUniqueMatches().size() - classMatches.getUniqueMatches().size() |
| 471 | )); | 430 | )); |
| 472 | 431 | ||
| 473 | // update the current matches | 432 | // update the current matches |
| 474 | classMatches = newMatches; | 433 | classMatches = newMatches; |
| 475 | save(); | 434 | save(); |
| 476 | updateMatches(); | 435 | updateMatches(); |
| 477 | } | 436 | } |
| 478 | 437 | ||
| 479 | private void advance() { | 438 | private void advance() { |
| 480 | advance(null); | 439 | advance(null); |
| 481 | } | 440 | } |
| 482 | 441 | ||
| 483 | private void advance(ClassEntry sourceClass) { | 442 | private void advance(ClassEntry sourceClass) { |
| 484 | 443 | ||
| 485 | // make sure we have a source class | 444 | // make sure we have a source class |
| 486 | if (sourceClass == null) { | 445 | if (sourceClass == null) { |
| 487 | sourceClass = sourceClasses.getSelectedClass(); | 446 | sourceClass = sourceClasses.getSelectedClass(); |
| 488 | if (sourceClass != null) { | 447 | if (sourceClass != null) { |
| 489 | sourceClass = sourceClasses.getNextClass(sourceClass); | 448 | sourceClass = sourceClasses.getNextClass(sourceClass); |
| 490 | } else { | 449 | } else { |
| 491 | sourceClass = sourceClasses.getFirstClass(); | 450 | sourceClass = sourceClasses.getFirstClass(); |
| 492 | } | 451 | } |
| 493 | } | 452 | } |
| 494 | 453 | ||
| 495 | // set the source class | 454 | // set the source class |
| 496 | setSourceClass(sourceClass, this::pickBestDestClass); | 455 | setSourceClass(sourceClass, this::pickBestDestClass); |
| 497 | sourceClasses.setSelectionClass(sourceClass); | 456 | sourceClasses.setSelectionClass(sourceClass); |
| 498 | } | 457 | } |
| 499 | 458 | ||
| 500 | private void pickBestDestClass() { | 459 | private void pickBestDestClass() { |
| 501 | 460 | ||
| 502 | // then, pick the best dest class | 461 | // then, pick the best dest class |
| 503 | ClassEntry firstClass = null; | 462 | ClassEntry firstClass = null; |
| 504 | ScoredClassEntry bestDestClass = null; | 463 | ScoredClassEntry bestDestClass = null; |
| 505 | for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { | 464 | for (ClassSelectorPackageNode packageNode : destClasses.packageNodes()) { |
| 506 | for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { | 465 | for (ClassSelectorClassNode classNode : destClasses.classNodes(packageNode)) { |
| 507 | if (firstClass == null) { | 466 | if (firstClass == null) { |
| 508 | firstClass = classNode.getClassEntry(); | 467 | firstClass = classNode.getClassEntry(); |
| 509 | } | 468 | } |
| 510 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { | 469 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { |
| 511 | ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); | 470 | ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry(); |
| 512 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { | 471 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { |
| 513 | bestDestClass = scoredClass; | 472 | bestDestClass = scoredClass; |
| 514 | } | 473 | } |
| 515 | } | 474 | } |
| 516 | } | 475 | } |
| 517 | } | 476 | } |
| 518 | 477 | ||
| 519 | // pick the entry to show | 478 | // pick the entry to show |
| 520 | ClassEntry destClass = null; | 479 | ClassEntry destClass = null; |
| 521 | if (bestDestClass != null) { | 480 | if (bestDestClass != null) { |
| 522 | destClass = bestDestClass; | 481 | destClass = bestDestClass; |
| 523 | } else if (firstClass != null) { | 482 | } else if (firstClass != null) { |
| 524 | destClass = firstClass; | 483 | destClass = firstClass; |
| 525 | } | 484 | } |
| 526 | 485 | ||
| 527 | setDestClass(destClass); | 486 | setDestClass(destClass); |
| 528 | destClasses.setSelectionClass(destClass); | 487 | destClasses.setSelectionClass(destClass); |
| 529 | } | 488 | } |
| 530 | 489 | ||
| 531 | private void toggleTop10Matches() { | 490 | private void toggleTop10Matches() { |
| 532 | if (sourceClass != null) { | 491 | if (sourceClass != null) { |
| 533 | destClasses.clearSelection(); | 492 | destClasses.clearSelection(); |
| 534 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); | 493 | destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator)); |
| 535 | destClasses.expandAll(); | 494 | destClasses.expandAll(); |
| 536 | } | 495 | } |
| 537 | } | 496 | } |
| 497 | |||
| 498 | private enum SourceType { | ||
| 499 | Matched { | ||
| 500 | @Override | ||
| 501 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 502 | return matches.getUniqueMatches().keySet(); | ||
| 503 | } | ||
| 504 | }, | ||
| 505 | Unmatched { | ||
| 506 | @Override | ||
| 507 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 508 | return matches.getUnmatchedSourceClasses(); | ||
| 509 | } | ||
| 510 | }, | ||
| 511 | Ambiguous { | ||
| 512 | @Override | ||
| 513 | public Collection<ClassEntry> getSourceClasses(ClassMatches matches) { | ||
| 514 | return matches.getAmbiguouslyMatchedSourceClasses(); | ||
| 515 | } | ||
| 516 | }; | ||
| 517 | |||
| 518 | public static SourceType getDefault() { | ||
| 519 | return values()[0]; | ||
| 520 | } | ||
| 521 | |||
| 522 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | ||
| 523 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | ||
| 524 | button.setActionCommand(name()); | ||
| 525 | button.addActionListener(listener); | ||
| 526 | group.add(button); | ||
| 527 | return button; | ||
| 528 | } | ||
| 529 | |||
| 530 | public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches); | ||
| 531 | } | ||
| 532 | |||
| 533 | public interface SaveListener { | ||
| 534 | void save(ClassMatches matches); | ||
| 535 | } | ||
| 538 | } | 536 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 8ece0a0a..f7d7703f 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.ArrayListMultimap; | 14 | import com.google.common.collect.ArrayListMultimap; |
| @@ -29,505 +30,470 @@ import java.util.*; | |||
| 29 | 30 | ||
| 30 | public class ClassSelector extends JTree { | 31 | public class ClassSelector extends JTree { |
| 31 | 32 | ||
| 32 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); | 33 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getName); |
| 33 | private DefaultMutableTreeNode rootNodes; | 34 | private DefaultMutableTreeNode rootNodes; |
| 34 | 35 | private ClassSelectionListener selectionListener; | |
| 35 | public interface ClassSelectionListener { | 36 | private RenameSelectionListener renameSelectionListener; |
| 36 | void onSelectClass(ClassEntry classEntry); | 37 | private Comparator<ClassEntry> comparator; |
| 37 | } | 38 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { |
| 38 | 39 | this.comparator = comparator; | |
| 39 | public interface RenameSelectionListener { | 40 | |
| 40 | void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node); | 41 | // configure the tree control |
| 41 | } | 42 | setEditable(gui != null); |
| 42 | 43 | setRootVisible(false); | |
| 43 | private ClassSelectionListener selectionListener; | 44 | setShowsRootHandles(false); |
| 44 | private RenameSelectionListener renameSelectionListener; | 45 | setModel(null); |
| 45 | private Comparator<ClassEntry> comparator; | 46 | |
| 46 | 47 | // hook events | |
| 47 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { | 48 | addMouseListener(new MouseAdapter() { |
| 48 | this.comparator = comparator; | 49 | @Override |
| 49 | 50 | public void mouseClicked(MouseEvent event) { | |
| 50 | // configure the tree control | 51 | if (selectionListener != null && event.getClickCount() == 2) { |
| 51 | setEditable(gui != null); | 52 | // get the selected node |
| 52 | setRootVisible(false); | 53 | TreePath path = getSelectionPath(); |
| 53 | setShowsRootHandles(false); | 54 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { |
| 54 | setModel(null); | 55 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); |
| 55 | 56 | selectionListener.onSelectClass(node.getClassEntry()); | |
| 56 | // hook events | 57 | } |
| 57 | addMouseListener(new MouseAdapter() { | 58 | } |
| 58 | @Override | 59 | } |
| 59 | public void mouseClicked(MouseEvent event) { | 60 | }); |
| 60 | if (selectionListener != null && event.getClickCount() == 2) { | 61 | |
| 61 | // get the selected node | 62 | if (gui != null) { |
| 62 | TreePath path = getSelectionPath(); | 63 | final JTree tree = this; |
| 63 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { | 64 | |
| 64 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); | 65 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, |
| 65 | selectionListener.onSelectClass(node.getClassEntry()); | 66 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { |
| 66 | } | 67 | @Override |
| 67 | } | 68 | public boolean isCellEditable(EventObject event) { |
| 68 | } | 69 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); |
| 69 | }); | 70 | } |
| 70 | 71 | }; | |
| 71 | if (gui != null) | 72 | this.setCellEditor(editor); |
| 72 | { | 73 | editor.addCellEditorListener(new CellEditorListener() { |
| 73 | final JTree tree = this; | 74 | @Override |
| 74 | 75 | public void editingStopped(ChangeEvent e) { | |
| 75 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, | 76 | String data = editor.getCellEditorValue().toString(); |
| 76 | (DefaultTreeCellRenderer) tree.getCellRenderer()) | 77 | TreePath path = getSelectionPath(); |
| 77 | { | 78 | |
| 78 | @Override public boolean isCellEditable(EventObject event) | 79 | Object realPath = path.getLastPathComponent(); |
| 79 | { | 80 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { |
| 80 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); | 81 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; |
| 81 | } | 82 | TreeNode parentNode = node.getParent(); |
| 82 | }; | 83 | if (parentNode == null) |
| 83 | this.setCellEditor(editor); | 84 | return; |
| 84 | editor.addCellEditorListener(new CellEditorListener() | 85 | boolean allowEdit = true; |
| 85 | { | 86 | for (int i = 0; i < parentNode.getChildCount(); i++) { |
| 86 | @Override public void editingStopped(ChangeEvent e) | 87 | TreeNode childNode = parentNode.getChildAt(i); |
| 87 | { | 88 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { |
| 88 | String data = editor.getCellEditorValue().toString(); | 89 | allowEdit = false; |
| 89 | TreePath path = getSelectionPath(); | 90 | break; |
| 90 | 91 | } | |
| 91 | Object realPath = path.getLastPathComponent(); | 92 | } |
| 92 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) | 93 | if (allowEdit && renameSelectionListener != null) { |
| 93 | { | 94 | Object prevData = node.getUserObject(); |
| 94 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; | 95 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; |
| 95 | TreeNode parentNode = node.getParent(); | 96 | try { |
| 96 | if (parentNode == null) | 97 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); |
| 97 | return; | 98 | node.setUserObject(objectData); // Make sure that it's modified |
| 98 | boolean allowEdit = true; | 99 | } catch (IllegalNameException ex) { |
| 99 | for (int i = 0; i < parentNode.getChildCount(); i++) | 100 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, |
| 100 | { | 101 | JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK"); |
| 101 | TreeNode childNode = parentNode.getChildAt(i); | 102 | editor.cancelCellEditing(); |
| 102 | if (childNode != null && childNode.toString().equals(data) && childNode != node) | 103 | } |
| 103 | { | 104 | } else |
| 104 | allowEdit = false; | 105 | editor.cancelCellEditing(); |
| 105 | break; | 106 | } |
| 106 | } | 107 | |
| 107 | } | 108 | } |
| 108 | if (allowEdit && renameSelectionListener != null) | 109 | |
| 109 | { | 110 | @Override |
| 110 | Object prevData = node.getUserObject(); | 111 | public void editingCanceled(ChangeEvent e) { |
| 111 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry)prevData).getPackageName() + "/" + data) : data; | 112 | // NOP |
| 112 | try | 113 | } |
| 113 | { | 114 | }); |
| 114 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | 115 | } |
| 115 | node.setUserObject(objectData); // Make sure that it's modified | 116 | // init defaults |
| 116 | } catch (IllegalNameException ex) | 117 | this.selectionListener = null; |
| 117 | { | 118 | this.renameSelectionListener = null; |
| 118 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | 119 | } |
| 119 | JOptionPane.ERROR_MESSAGE, null, new String[] {"Ok"}, "OK"); | 120 | |
| 120 | editor.cancelCellEditing(); | 121 | public boolean isDuplicate(Object[] nodes, String data) { |
| 121 | } | 122 | int count = 0; |
| 122 | } | 123 | |
| 123 | else | 124 | for (Object node : nodes) { |
| 124 | editor.cancelCellEditing(); | 125 | if (node.toString().equals(data)) { |
| 125 | } | 126 | count++; |
| 126 | 127 | if (count == 2) | |
| 127 | } | 128 | return true; |
| 128 | 129 | } | |
| 129 | @Override public void editingCanceled(ChangeEvent e) | 130 | } |
| 130 | { | 131 | return false; |
| 131 | // NOP | 132 | } |
| 132 | } | 133 | |
| 133 | }); | 134 | public void setSelectionListener(ClassSelectionListener val) { |
| 134 | } | 135 | this.selectionListener = val; |
| 135 | // init defaults | 136 | } |
| 136 | this.selectionListener = null; | 137 | |
| 137 | this.renameSelectionListener = null; | 138 | public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) { |
| 138 | } | 139 | this.renameSelectionListener = renameSelectionListener; |
| 139 | 140 | } | |
| 140 | public boolean isDuplicate(Object[] nodes, String data) | 141 | |
| 141 | { | 142 | public void setClasses(Collection<ClassEntry> classEntries) { |
| 142 | int count = 0; | 143 | String state = getExpansionState(this, 0); |
| 143 | 144 | if (classEntries == null) { | |
| 144 | for (Object node : nodes) | 145 | setModel(null); |
| 145 | { | 146 | return; |
| 146 | if (node.toString().equals(data)) | 147 | } |
| 147 | { | 148 | |
| 148 | count++; | 149 | // build the package names |
| 149 | if (count == 2) | 150 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); |
| 150 | return true; | 151 | for (ClassEntry classEntry : classEntries) { |
| 151 | } | 152 | packages.put(classEntry.getPackageName(), null); |
| 152 | } | 153 | } |
| 153 | return false; | 154 | |
| 154 | } | 155 | // sort the packages |
| 155 | 156 | List<String> sortedPackageNames = Lists.newArrayList(packages.keySet()); | |
| 156 | public void setSelectionListener(ClassSelectionListener val) { | 157 | sortedPackageNames.sort((a, b) -> |
| 157 | this.selectionListener = val; | 158 | { |
| 158 | } | 159 | // I can never keep this rule straight when writing these damn things... |
| 159 | 160 | // a < b => -1, a == b => 0, a > b => +1 | |
| 160 | public void setRenameSelectionListener(RenameSelectionListener renameSelectionListener) | 161 | |
| 161 | { | 162 | if (b == null || a == null) { |
| 162 | this.renameSelectionListener = renameSelectionListener; | 163 | return 0; |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | public void setClasses(Collection<ClassEntry> classEntries) { | 166 | String[] aparts = a.split("/"); |
| 166 | String state = getExpansionState(this, 0); | 167 | String[] bparts = b.split("/"); |
| 167 | if (classEntries == null) { | 168 | for (int i = 0; true; i++) { |
| 168 | setModel(null); | 169 | if (i >= aparts.length) { |
| 169 | return; | 170 | return -1; |
| 170 | } | 171 | } else if (i >= bparts.length) { |
| 171 | 172 | return 1; | |
| 172 | // build the package names | 173 | } |
| 173 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); | 174 | |
| 174 | for (ClassEntry classEntry : classEntries) { | 175 | int result = aparts[i].compareTo(bparts[i]); |
| 175 | packages.put(classEntry.getPackageName(), null); | 176 | if (result != 0) { |
| 176 | } | 177 | return result; |
| 177 | 178 | } | |
| 178 | // sort the packages | 179 | } |
| 179 | List<String> sortedPackageNames = Lists.newArrayList(packages.keySet()); | 180 | }); |
| 180 | sortedPackageNames.sort((a, b) -> | 181 | |
| 181 | { | 182 | // create the rootNodes node and the package nodes |
| 182 | // I can never keep this rule straight when writing these damn things... | 183 | rootNodes = new DefaultMutableTreeNode(); |
| 183 | // a < b => -1, a == b => 0, a > b => +1 | 184 | for (String packageName : sortedPackageNames) { |
| 184 | 185 | ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); | |
| 185 | if (b == null || a == null) | 186 | packages.put(packageName, node); |
| 186 | { | 187 | rootNodes.add(node); |
| 187 | return 0; | 188 | } |
| 188 | } | 189 | |
| 189 | 190 | // put the classes into packages | |
| 190 | String[] aparts = a.split("/"); | 191 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); |
| 191 | String[] bparts = b.split("/"); | 192 | for (ClassEntry classEntry : classEntries) { |
| 192 | for (int i = 0; true; i++) | 193 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); |
| 193 | { | 194 | } |
| 194 | if (i >= aparts.length) | 195 | |
| 195 | { | 196 | // build the class nodes |
| 196 | return -1; | 197 | for (String packageName : packagedClassEntries.keySet()) { |
| 197 | } | 198 | // sort the class entries |
| 198 | else if (i >= bparts.length) | 199 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); |
| 199 | { | 200 | classEntriesInPackage.sort(this.comparator); |
| 200 | return 1; | 201 | |
| 201 | } | 202 | // create the nodes in order |
| 202 | 203 | for (ClassEntry classEntry : classEntriesInPackage) { | |
| 203 | int result = aparts[i].compareTo(bparts[i]); | 204 | ClassSelectorPackageNode node = packages.get(packageName); |
| 204 | if (result != 0) | 205 | node.add(new ClassSelectorClassNode(classEntry)); |
| 205 | { | 206 | } |
| 206 | return result; | 207 | } |
| 207 | } | 208 | |
| 208 | } | 209 | // finally, update the tree control |
| 209 | }); | 210 | setModel(new DefaultTreeModel(rootNodes)); |
| 210 | 211 | ||
| 211 | // create the rootNodes node and the package nodes | 212 | restoreExpanstionState(this, 0, state); |
| 212 | rootNodes = new DefaultMutableTreeNode(); | 213 | } |
| 213 | for (String packageName : sortedPackageNames) { | 214 | |
| 214 | ClassSelectorPackageNode node = new ClassSelectorPackageNode(packageName); | 215 | public ClassEntry getSelectedClass() { |
| 215 | packages.put(packageName, node); | 216 | if (!isSelectionEmpty()) { |
| 216 | rootNodes.add(node); | 217 | Object selectedNode = getSelectionPath().getLastPathComponent(); |
| 217 | } | 218 | if (selectedNode instanceof ClassSelectorClassNode) { |
| 218 | 219 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode; | |
| 219 | // put the classes into packages | 220 | return classNode.getClassEntry(); |
| 220 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); | 221 | } |
| 221 | for (ClassEntry classEntry : classEntries) { | 222 | } |
| 222 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); | 223 | return null; |
| 223 | } | 224 | } |
| 224 | 225 | ||
| 225 | // build the class nodes | 226 | public String getSelectedPackage() { |
| 226 | for (String packageName : packagedClassEntries.keySet()) { | 227 | if (!isSelectionEmpty()) { |
| 227 | // sort the class entries | 228 | Object selectedNode = getSelectionPath().getLastPathComponent(); |
| 228 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); | 229 | if (selectedNode instanceof ClassSelectorPackageNode) { |
| 229 | classEntriesInPackage.sort(this.comparator); | 230 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) selectedNode; |
| 230 | 231 | return packageNode.getPackageName(); | |
| 231 | // create the nodes in order | 232 | } else if (selectedNode instanceof ClassSelectorClassNode) { |
| 232 | for (ClassEntry classEntry : classEntriesInPackage) { | 233 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode; |
| 233 | ClassSelectorPackageNode node = packages.get(packageName); | 234 | return classNode.getClassEntry().getPackageName(); |
| 234 | node.add(new ClassSelectorClassNode(classEntry)); | 235 | } |
| 235 | } | 236 | } |
| 236 | } | 237 | return null; |
| 237 | 238 | } | |
| 238 | // finally, update the tree control | 239 | |
| 239 | setModel(new DefaultTreeModel(rootNodes)); | 240 | public boolean isDescendant(TreePath path1, TreePath path2) { |
| 240 | 241 | int count1 = path1.getPathCount(); | |
| 241 | restoreExpanstionState(this, 0, state); | 242 | int count2 = path2.getPathCount(); |
| 242 | } | 243 | if (count1 <= count2) { |
| 243 | 244 | return false; | |
| 244 | public ClassEntry getSelectedClass() { | 245 | } |
| 245 | if (!isSelectionEmpty()) { | 246 | while (count1 != count2) { |
| 246 | Object selectedNode = getSelectionPath().getLastPathComponent(); | 247 | path1 = path1.getParentPath(); |
| 247 | if (selectedNode instanceof ClassSelectorClassNode) { | 248 | count1--; |
| 248 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | 249 | } |
| 249 | return classNode.getClassEntry(); | 250 | return path1.equals(path2); |
| 250 | } | 251 | } |
| 251 | } | 252 | |
| 252 | return null; | 253 | public String getExpansionState(JTree tree, int row) { |
| 253 | } | 254 | TreePath rowPath = tree.getPathForRow(row); |
| 254 | 255 | StringBuilder buf = new StringBuilder(); | |
| 255 | public String getSelectedPackage() { | 256 | int rowCount = tree.getRowCount(); |
| 256 | if (!isSelectionEmpty()) { | 257 | for (int i = row; i < rowCount; i++) { |
| 257 | Object selectedNode = getSelectionPath().getLastPathComponent(); | 258 | TreePath path = tree.getPathForRow(i); |
| 258 | if (selectedNode instanceof ClassSelectorPackageNode) { | 259 | if (i == row || isDescendant(path, rowPath)) { |
| 259 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)selectedNode; | 260 | if (tree.isExpanded(path)) { |
| 260 | return packageNode.getPackageName(); | 261 | buf.append(",").append((i - row)); |
| 261 | } else if (selectedNode instanceof ClassSelectorClassNode) { | 262 | } |
| 262 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | 263 | } else { |
| 263 | return classNode.getClassEntry().getPackageName(); | 264 | break; |
| 264 | } | 265 | } |
| 265 | } | 266 | } |
| 266 | return null; | 267 | return buf.toString(); |
| 267 | } | 268 | } |
| 268 | 269 | ||
| 269 | public boolean isDescendant(TreePath path1, TreePath path2) { | 270 | public void restoreExpanstionState(JTree tree, int row, String expansionState) { |
| 270 | int count1 = path1.getPathCount(); | 271 | StringTokenizer stok = new StringTokenizer(expansionState, ","); |
| 271 | int count2 = path2.getPathCount(); | 272 | while (stok.hasMoreTokens()) { |
| 272 | if (count1 <= count2) { | 273 | int token = row + Integer.parseInt(stok.nextToken()); |
| 273 | return false; | 274 | tree.expandRow(token); |
| 274 | } | 275 | } |
| 275 | while (count1 != count2) { | 276 | } |
| 276 | path1 = path1.getParentPath(); | 277 | |
| 277 | count1--; | 278 | public List<ClassSelectorPackageNode> packageNodes() { |
| 278 | } | 279 | List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); |
| 279 | return path1.equals(path2); | 280 | DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot(); |
| 280 | } | 281 | Enumeration<?> children = root.children(); |
| 281 | 282 | while (children.hasMoreElements()) { | |
| 282 | public String getExpansionState(JTree tree, int row) { | 283 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) children.nextElement(); |
| 283 | TreePath rowPath = tree.getPathForRow(row); | 284 | nodes.add(packageNode); |
| 284 | StringBuilder buf = new StringBuilder(); | 285 | } |
| 285 | int rowCount = tree.getRowCount(); | 286 | return nodes; |
| 286 | for (int i = row; i < rowCount; i++) { | 287 | } |
| 287 | TreePath path = tree.getPathForRow(i); | 288 | |
| 288 | if (i == row || isDescendant(path, rowPath)) { | 289 | public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { |
| 289 | if (tree.isExpanded(path)) { | 290 | List<ClassSelectorClassNode> nodes = Lists.newArrayList(); |
| 290 | buf.append(",").append(String.valueOf(i - row)); | 291 | Enumeration<?> children = packageNode.children(); |
| 291 | } | 292 | while (children.hasMoreElements()) { |
| 292 | } else { | 293 | ClassSelectorClassNode classNode = (ClassSelectorClassNode) children.nextElement(); |
| 293 | break; | 294 | nodes.add(classNode); |
| 294 | } | 295 | } |
| 295 | } | 296 | return nodes; |
| 296 | return buf.toString(); | 297 | } |
| 297 | } | 298 | |
| 298 | 299 | public void expandPackage(String packageName) { | |
| 299 | public void restoreExpanstionState(JTree tree, int row, String expansionState) { | 300 | if (packageName == null) { |
| 300 | StringTokenizer stok = new StringTokenizer(expansionState, ","); | 301 | return; |
| 301 | while (stok.hasMoreTokens()) { | 302 | } |
| 302 | int token = row + Integer.parseInt(stok.nextToken()); | 303 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 303 | tree.expandRow(token); | 304 | if (packageNode.getPackageName().equals(packageName)) { |
| 304 | } | 305 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); |
| 305 | } | 306 | return; |
| 306 | 307 | } | |
| 307 | public List<ClassSelectorPackageNode> packageNodes() { | 308 | } |
| 308 | List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); | 309 | } |
| 309 | DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); | 310 | |
| 310 | Enumeration<?> children = root.children(); | 311 | public void expandAll() { |
| 311 | while (children.hasMoreElements()) { | 312 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 312 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)children.nextElement(); | 313 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); |
| 313 | nodes.add(packageNode); | 314 | } |
| 314 | } | 315 | } |
| 315 | return nodes; | 316 | |
| 316 | } | 317 | public ClassEntry getFirstClass() { |
| 317 | 318 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | |
| 318 | public List<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { | 319 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 319 | List<ClassSelectorClassNode> nodes = Lists.newArrayList(); | 320 | return classNode.getClassEntry(); |
| 320 | Enumeration<?> children = packageNode.children(); | 321 | } |
| 321 | while (children.hasMoreElements()) { | 322 | } |
| 322 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)children.nextElement(); | 323 | return null; |
| 323 | nodes.add(classNode); | 324 | } |
| 324 | } | 325 | |
| 325 | return nodes; | 326 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { |
| 326 | } | 327 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 327 | 328 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | |
| 328 | public void expandPackage(String packageName) { | 329 | return packageNode; |
| 329 | if (packageName == null) { | 330 | } |
| 330 | return; | 331 | } |
| 331 | } | 332 | return null; |
| 332 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 333 | } |
| 333 | if (packageNode.getPackageName().equals(packageName)) { | 334 | |
| 334 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | 335 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { |
| 335 | return; | 336 | ClassSelectorPackageNode packageNode = getPackageNode(entry); |
| 336 | } | 337 | |
| 337 | } | 338 | if (selector != null && packageNode == null && selector.getPackageNode(entry) != null) |
| 338 | } | 339 | return selector.getPackageNode(entry); |
| 339 | 340 | return packageNode; | |
| 340 | public void expandAll() { | 341 | } |
| 341 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 342 | |
| 342 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | 343 | public ClassEntry getNextClass(ClassEntry entry) { |
| 343 | } | 344 | boolean foundIt = false; |
| 344 | } | 345 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 345 | 346 | if (!foundIt) { | |
| 346 | public ClassEntry getFirstClass() { | 347 | // skip to the package with our target in it |
| 347 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 348 | if (packageNode.getPackageName().equals(entry.getPackageName())) { |
| 348 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 349 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 349 | return classNode.getClassEntry(); | 350 | if (!foundIt) { |
| 350 | } | 351 | if (classNode.getClassEntry().equals(entry)) { |
| 351 | } | 352 | foundIt = true; |
| 352 | return null; | 353 | } |
| 353 | } | 354 | } else { |
| 354 | 355 | // return the next class | |
| 355 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { | 356 | return classNode.getClassEntry(); |
| 356 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 357 | } |
| 357 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | 358 | } |
| 358 | return packageNode; | 359 | } |
| 359 | } | 360 | } else { |
| 360 | } | 361 | // return the next class |
| 361 | return null; | 362 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 362 | } | 363 | return classNode.getClassEntry(); |
| 363 | 364 | } | |
| 364 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) | 365 | } |
| 365 | { | 366 | } |
| 366 | ClassSelectorPackageNode packageNode = getPackageNode(entry); | 367 | return null; |
| 367 | 368 | } | |
| 368 | if (selector != null && packageNode == null && selector.getPackageNode(entry) != null) | 369 | |
| 369 | return selector.getPackageNode(entry); | 370 | public void setSelectionClass(ClassEntry classEntry) { |
| 370 | return packageNode; | 371 | expandPackage(classEntry.getPackageName()); |
| 371 | } | 372 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 372 | 373 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | |
| 373 | public ClassEntry getNextClass(ClassEntry entry) { | 374 | if (classNode.getClassEntry().equals(classEntry)) { |
| 374 | boolean foundIt = false; | 375 | setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode })); |
| 375 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 376 | } |
| 376 | if (!foundIt) { | 377 | } |
| 377 | // skip to the package with our target in it | 378 | } |
| 378 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | 379 | } |
| 379 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 380 | |
| 380 | if (!foundIt) { | 381 | public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { |
| 381 | if (classNode.getClassEntry().equals(entry)) { | 382 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 382 | foundIt = true; | 383 | |
| 383 | } | 384 | if (packageNode == null) |
| 384 | } else { | 385 | return; |
| 385 | // return the next class | 386 | |
| 386 | return classNode.getClassEntry(); | 387 | for (int i = 0; i < packageNode.getChildCount(); i++) { |
| 387 | } | 388 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); |
| 388 | } | 389 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { |
| 389 | } | 390 | model.removeNodeFromParent(childNode); |
| 390 | } else { | 391 | break; |
| 391 | // return the next class | 392 | } |
| 392 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 393 | } |
| 393 | return classNode.getClassEntry(); | 394 | } |
| 394 | } | 395 | |
| 395 | } | 396 | public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) { |
| 396 | } | 397 | if (packageNode != null && packageNode.getChildCount() == 0) |
| 397 | return null; | 398 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); |
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | public void setSelectionClass(ClassEntry classEntry) { | 401 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) { |
| 401 | expandPackage(classEntry.getPackageName()); | 402 | if (otherSelector == null) |
| 402 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 403 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); |
| 403 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 404 | insertNode(getOrCreate(newClassEntry), newClassEntry); |
| 404 | if (classNode.getClassEntry().equals(classEntry)) { | 405 | } |
| 405 | setSelectionPath(new TreePath(new Object[] {getModel().getRoot(), packageNode, classNode})); | 406 | |
| 406 | } | 407 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) { |
| 407 | } | 408 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 408 | } | 409 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); |
| 409 | } | 410 | if (newPackageNode == null) { |
| 410 | 411 | newPackageNode = new ClassSelectorPackageNode(entry.getPackageName()); | |
| 411 | public void removeNode(ClassSelectorPackageNode packageNode, ClassEntry entry) | 412 | model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode)); |
| 412 | { | 413 | } |
| 413 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 414 | return newPackageNode; |
| 414 | 415 | } | |
| 415 | if (packageNode == null) | 416 | |
| 416 | return; | 417 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { |
| 417 | 418 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | |
| 418 | for (int i = 0; i < packageNode.getChildCount(); i++) | 419 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); |
| 419 | { | 420 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); |
| 420 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); | 421 | } |
| 421 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) | 422 | |
| 422 | { | 423 | public void reload() { |
| 423 | model.removeNodeFromParent(childNode); | 424 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 424 | break; | 425 | model.reload(sort(rootNodes)); |
| 425 | } | 426 | } |
| 426 | } | 427 | |
| 427 | } | 428 | private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { |
| 428 | 429 | ||
| 429 | public void removeNodeIfEmpty(ClassSelectorPackageNode packageNode) | 430 | for (int i = 0; i < node.getChildCount() - 1; i++) { |
| 430 | { | 431 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); |
| 431 | if (packageNode != null && packageNode.getChildCount() == 0) | 432 | if (child == null) |
| 432 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); | 433 | continue; |
| 433 | } | 434 | String nt = child.toString(); |
| 434 | 435 | ||
| 435 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) | 436 | for (int j = i + 1; j <= node.getChildCount() - 1; j++) { |
| 436 | { | 437 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); |
| 437 | if (otherSelector == null) | 438 | if (prevNode == null || prevNode.getUserObject() == null) |
| 438 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); | 439 | continue; |
| 439 | insertNode(getOrCreate(newClassEntry), newClassEntry); | 440 | String np = prevNode.getUserObject().toString(); |
| 440 | } | 441 | |
| 441 | 442 | if (nt.compareToIgnoreCase(np) > 0) { | |
| 442 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) | 443 | node.insert(child, j); |
| 443 | { | 444 | node.insert(prevNode, i); |
| 444 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 445 | } |
| 445 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); | 446 | } |
| 446 | if (newPackageNode == null) | 447 | if (child.getChildCount() > 0) { |
| 447 | { | 448 | sort(child); |
| 448 | newPackageNode = new ClassSelectorPackageNode(entry.getPackageName()); | 449 | } |
| 449 | model.insertNodeInto(newPackageNode, (MutableTreeNode) model.getRoot(), getPlacementIndex(newPackageNode)); | 450 | } |
| 450 | } | 451 | |
| 451 | return newPackageNode; | 452 | for (int i = 0; i < node.getChildCount() - 1; i++) { |
| 452 | } | 453 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); |
| 453 | 454 | for (int j = i + 1; j <= node.getChildCount() - 1; j++) { | |
| 454 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) | 455 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); |
| 455 | { | 456 | |
| 456 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 457 | if (!prevNode.isLeaf() && child.isLeaf()) { |
| 457 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); | 458 | node.insert(child, j); |
| 458 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); | 459 | node.insert(prevNode, i); |
| 459 | } | 460 | } |
| 460 | 461 | } | |
| 461 | public void reload() | 462 | } |
| 462 | { | 463 | |
| 463 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 464 | return node; |
| 464 | model.reload(sort(rootNodes)); | 465 | } |
| 465 | } | 466 | |
| 466 | 467 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) { | |
| 467 | private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) { | 468 | List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode); |
| 468 | 469 | classNodes.add(classNode); | |
| 469 | for(int i = 0; i < node.getChildCount() - 1; i++) { | 470 | classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString)); |
| 470 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); | 471 | for (int i = 0; i < classNodes.size(); i++) |
| 471 | if (child == null) | 472 | if (classNodes.get(i) == classNode) |
| 472 | continue; | 473 | return i; |
| 473 | String nt = child.toString(); | 474 | |
| 474 | 475 | return 0; | |
| 475 | for(int j = i + 1; j <= node.getChildCount() - 1; j++) { | 476 | } |
| 476 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); | 477 | |
| 477 | if (prevNode == null || prevNode.getUserObject() == null) | 478 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode) { |
| 478 | continue; | 479 | List<ClassSelectorPackageNode> packageNodes = packageNodes(); |
| 479 | String np = prevNode.getUserObject().toString(); | 480 | if (!packageNodes.contains(newPackageNode)) { |
| 480 | 481 | packageNodes.add(newPackageNode); | |
| 481 | if(nt.compareToIgnoreCase(np) > 0) { | 482 | packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString)); |
| 482 | node.insert(child, j); | 483 | } |
| 483 | node.insert(prevNode, i); | 484 | |
| 484 | } | 485 | for (int i = 0; i < packageNodes.size(); i++) |
| 485 | } | 486 | if (packageNodes.get(i) == newPackageNode) |
| 486 | if(child.getChildCount() > 0) { | 487 | return i; |
| 487 | sort(child); | 488 | |
| 488 | } | 489 | return 0; |
| 489 | } | 490 | } |
| 490 | 491 | ||
| 491 | for(int i = 0; i < node.getChildCount() - 1; i++) { | 492 | public interface ClassSelectionListener { |
| 492 | DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); | 493 | void onSelectClass(ClassEntry classEntry); |
| 493 | for(int j = i + 1; j <= node.getChildCount() - 1; j++) { | 494 | } |
| 494 | DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j); | 495 | |
| 495 | 496 | public interface RenameSelectionListener { | |
| 496 | if(!prevNode.isLeaf() && child.isLeaf()) { | 497 | void onSelectionRename(Object prevData, Object data, DefaultMutableTreeNode node); |
| 497 | node.insert(child, j); | 498 | } |
| 498 | node.insert(prevNode, i); | ||
| 499 | } | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | return node; | ||
| 504 | } | ||
| 505 | |||
| 506 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) | ||
| 507 | { | ||
| 508 | List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode); | ||
| 509 | classNodes.add(classNode); | ||
| 510 | classNodes.sort(Comparator.comparing(ClassSelectorClassNode::toString)); | ||
| 511 | for (int i = 0; i < classNodes.size(); i++) | ||
| 512 | if (classNodes.get(i) == classNode) | ||
| 513 | return i; | ||
| 514 | |||
| 515 | return 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | private int getPlacementIndex(ClassSelectorPackageNode newPackageNode) | ||
| 519 | { | ||
| 520 | List<ClassSelectorPackageNode> packageNodes = packageNodes(); | ||
| 521 | if (!packageNodes.contains(newPackageNode)) | ||
| 522 | { | ||
| 523 | packageNodes.add(newPackageNode); | ||
| 524 | packageNodes.sort(Comparator.comparing(ClassSelectorPackageNode::toString)); | ||
| 525 | } | ||
| 526 | |||
| 527 | for (int i = 0; i < packageNodes.size(); i++) | ||
| 528 | if (packageNodes.get(i) == newPackageNode) | ||
| 529 | return i; | ||
| 530 | |||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | } | 499 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java index 8225d8f4..2e235dc6 100644 --- a/src/main/java/cuchaz/enigma/gui/CodeReader.java +++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java | |||
| @@ -8,20 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 14 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 14 | |||
| 15 | import java.awt.Rectangle; | ||
| 16 | import java.awt.event.ActionEvent; | ||
| 17 | import java.awt.event.ActionListener; | ||
| 18 | |||
| 19 | import javax.swing.JEditorPane; | ||
| 20 | import javax.swing.SwingUtilities; | ||
| 21 | import javax.swing.Timer; | ||
| 22 | import javax.swing.text.BadLocationException; | ||
| 23 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 24 | |||
| 25 | import cuchaz.enigma.Deobfuscator; | 15 | import cuchaz.enigma.Deobfuscator; |
| 26 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 27 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.analysis.SourceIndex; |
| @@ -31,180 +21,184 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 31 | import cuchaz.enigma.mapping.Entry; | 21 | import cuchaz.enigma.mapping.Entry; |
| 32 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 22 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 33 | 23 | ||
| 24 | import javax.swing.*; | ||
| 25 | import javax.swing.text.BadLocationException; | ||
| 26 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 27 | import java.awt.*; | ||
| 28 | import java.awt.event.ActionEvent; | ||
| 29 | import java.awt.event.ActionListener; | ||
| 34 | 30 | ||
| 35 | public class CodeReader extends JEditorPane { | 31 | public class CodeReader extends JEditorPane { |
| 36 | 32 | ||
| 37 | private static final long serialVersionUID = 3673180950485748810L; | 33 | private static final long serialVersionUID = 3673180950485748810L; |
| 38 | 34 | ||
| 39 | private static final Object lock = new Object(); | 35 | private static final Object lock = new Object(); |
| 40 | 36 | private SelectionHighlightPainter selectionHighlightPainter; | |
| 41 | public interface SelectionListener { | 37 | private SourceIndex sourceIndex; |
| 42 | void onSelect(EntryReference<Entry, Entry> reference); | 38 | private SelectionListener selectionListener; |
| 43 | } | 39 | public CodeReader() { |
| 44 | 40 | ||
| 45 | private SelectionHighlightPainter selectionHighlightPainter; | 41 | setEditable(false); |
| 46 | private SourceIndex sourceIndex; | 42 | setContentType("text/java"); |
| 47 | private SelectionListener selectionListener; | 43 | |
| 48 | 44 | // turn off token highlighting (it's wrong most of the time anyway...) | |
| 49 | public CodeReader() { | 45 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); |
| 50 | 46 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | |
| 51 | setEditable(false); | 47 | |
| 52 | setContentType("text/java"); | 48 | // hook events |
| 53 | 49 | addCaretListener(event -> | |
| 54 | // turn off token highlighting (it's wrong most of the time anyway...) | 50 | { |
| 55 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); | 51 | if (selectionListener != null && sourceIndex != null) { |
| 56 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | 52 | Token token = sourceIndex.getReferenceToken(event.getDot()); |
| 57 | 53 | if (token != null) { | |
| 58 | // hook events | 54 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); |
| 59 | addCaretListener(event -> | 55 | } else { |
| 60 | { | 56 | selectionListener.onSelect(null); |
| 61 | if (selectionListener != null && sourceIndex != null) { | 57 | } |
| 62 | Token token = sourceIndex.getReferenceToken(event.getDot()); | 58 | } |
| 63 | if (token != null) { | 59 | }); |
| 64 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); | 60 | |
| 65 | } else { | 61 | selectionHighlightPainter = new SelectionHighlightPainter(); |
| 66 | selectionListener.onSelect(null); | 62 | } |
| 67 | } | 63 | |
| 68 | } | 64 | // HACKHACK: someday we can update the main GUI to use this code reader |
| 69 | }); | 65 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { |
| 70 | 66 | ||
| 71 | selectionHighlightPainter = new SelectionHighlightPainter(); | 67 | // set the caret position to the token |
| 72 | } | 68 | editor.setCaretPosition(token.start); |
| 73 | 69 | editor.grabFocus(); | |
| 74 | public void setSelectionListener(SelectionListener val) { | 70 | |
| 75 | selectionListener = val; | 71 | try { |
| 76 | } | 72 | // make sure the token is visible in the scroll window |
| 77 | 73 | Rectangle start = editor.modelToView(token.start); | |
| 78 | public void setCode(String code) { | 74 | Rectangle end = editor.modelToView(token.end); |
| 79 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls | 75 | final Rectangle show = start.union(end); |
| 80 | synchronized (lock) { | 76 | show.grow(start.width * 10, start.height * 6); |
| 81 | setText(code); | 77 | SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show)); |
| 82 | } | 78 | } catch (BadLocationException ex) { |
| 83 | } | 79 | throw new Error(ex); |
| 84 | 80 | } | |
| 85 | public SourceIndex getSourceIndex() { | 81 | |
| 86 | return sourceIndex; | 82 | // highlight the token momentarily |
| 87 | } | 83 | final Timer timer = new Timer(200, new ActionListener() { |
| 88 | 84 | private int counter = 0; | |
| 89 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { | 85 | private Object highlight = null; |
| 90 | decompileClass(classEntry, deobfuscator, null); | 86 | |
| 91 | } | 87 | @Override |
| 92 | 88 | public void actionPerformed(ActionEvent event) { | |
| 93 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { | 89 | if (counter % 2 == 0) { |
| 94 | decompileClass(classEntry, deobfuscator, null, callback); | 90 | try { |
| 95 | } | 91 | highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); |
| 96 | 92 | } catch (BadLocationException ex) { | |
| 97 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { | 93 | // don't care |
| 98 | 94 | } | |
| 99 | if (classEntry == null) { | 95 | } else if (highlight != null) { |
| 100 | setCode(null); | 96 | editor.getHighlighter().removeHighlight(highlight); |
| 101 | return; | 97 | } |
| 102 | } | 98 | |
| 103 | 99 | if (counter++ > 6) { | |
| 104 | setCode("(decompiling...)"); | 100 | Timer timer = (Timer) event.getSource(); |
| 105 | 101 | timer.stop(); | |
| 106 | // run decompilation in a separate thread to keep ui responsive | 102 | } |
| 107 | new Thread(() -> | 103 | } |
| 108 | { | 104 | }); |
| 109 | 105 | timer.start(); | |
| 110 | // decompile it | 106 | } |
| 111 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); | 107 | |
| 112 | String source = deobfuscator.getSource(sourceTree); | 108 | public void setSelectionListener(SelectionListener val) { |
| 113 | setCode(source); | 109 | selectionListener = val; |
| 114 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); | 110 | } |
| 115 | 111 | ||
| 116 | if (callback != null) { | 112 | public void setCode(String code) { |
| 117 | callback.run(); | 113 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls |
| 118 | } | 114 | synchronized (lock) { |
| 119 | }).start(); | 115 | setText(code); |
| 120 | } | 116 | } |
| 121 | 117 | } | |
| 122 | public void navigateToClassDeclaration(ClassEntry classEntry) { | 118 | |
| 123 | 119 | public SourceIndex getSourceIndex() { | |
| 124 | // navigate to the class declaration | 120 | return sourceIndex; |
| 125 | Token token = sourceIndex.getDeclarationToken(classEntry); | 121 | } |
| 126 | if (token == null) { | 122 | |
| 127 | // couldn't find the class declaration token, might be an anonymous class | 123 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { |
| 128 | // look for any declaration in that class instead | 124 | decompileClass(classEntry, deobfuscator, null); |
| 129 | for (Entry entry : sourceIndex.declarations()) { | 125 | } |
| 130 | if (entry.getClassEntry().equals(classEntry)) { | 126 | |
| 131 | token = sourceIndex.getDeclarationToken(entry); | 127 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { |
| 132 | break; | 128 | decompileClass(classEntry, deobfuscator, null, callback); |
| 133 | } | 129 | } |
| 134 | } | 130 | |
| 135 | } | 131 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { |
| 136 | 132 | ||
| 137 | if (token != null) { | 133 | if (classEntry == null) { |
| 138 | navigateToToken(token); | 134 | setCode(null); |
| 139 | } else { | 135 | return; |
| 140 | // couldn't find anything =( | 136 | } |
| 141 | System.out.println("Unable to find declaration in source for " + classEntry); | 137 | |
| 142 | } | 138 | setCode("(decompiling...)"); |
| 143 | } | 139 | |
| 144 | 140 | // run decompilation in a separate thread to keep ui responsive | |
| 145 | public void navigateToToken(final Token token) { | 141 | new Thread(() -> |
| 146 | navigateToToken(this, token, selectionHighlightPainter); | 142 | { |
| 147 | } | 143 | |
| 148 | 144 | // decompile it | |
| 149 | // HACKHACK: someday we can update the main GUI to use this code reader | 145 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName()); |
| 150 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { | 146 | String source = deobfuscator.getSource(sourceTree); |
| 151 | 147 | setCode(source); | |
| 152 | // set the caret position to the token | 148 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); |
| 153 | editor.setCaretPosition(token.start); | 149 | |
| 154 | editor.grabFocus(); | 150 | if (callback != null) { |
| 155 | 151 | callback.run(); | |
| 156 | try { | 152 | } |
| 157 | // make sure the token is visible in the scroll window | 153 | }).start(); |
| 158 | Rectangle start = editor.modelToView(token.start); | 154 | } |
| 159 | Rectangle end = editor.modelToView(token.end); | 155 | |
| 160 | final Rectangle show = start.union(end); | 156 | public void navigateToClassDeclaration(ClassEntry classEntry) { |
| 161 | show.grow(start.width * 10, start.height * 6); | 157 | |
| 162 | SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show)); | 158 | // navigate to the class declaration |
| 163 | } catch (BadLocationException ex) { | 159 | Token token = sourceIndex.getDeclarationToken(classEntry); |
| 164 | throw new Error(ex); | 160 | if (token == null) { |
| 165 | } | 161 | // couldn't find the class declaration token, might be an anonymous class |
| 166 | 162 | // look for any declaration in that class instead | |
| 167 | // highlight the token momentarily | 163 | for (Entry entry : sourceIndex.declarations()) { |
| 168 | final Timer timer = new Timer(200, new ActionListener() { | 164 | if (entry.getClassEntry().equals(classEntry)) { |
| 169 | private int counter = 0; | 165 | token = sourceIndex.getDeclarationToken(entry); |
| 170 | private Object highlight = null; | 166 | break; |
| 171 | 167 | } | |
| 172 | @Override | 168 | } |
| 173 | public void actionPerformed(ActionEvent event) { | 169 | } |
| 174 | if (counter % 2 == 0) { | 170 | |
| 175 | try { | 171 | if (token != null) { |
| 176 | highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); | 172 | navigateToToken(token); |
| 177 | } catch (BadLocationException ex) { | 173 | } else { |
| 178 | // don't care | 174 | // couldn't find anything =( |
| 179 | } | 175 | System.out.println("Unable to find declaration in source for " + classEntry); |
| 180 | } else if (highlight != null) { | 176 | } |
| 181 | editor.getHighlighter().removeHighlight(highlight); | 177 | } |
| 182 | } | 178 | |
| 183 | 179 | public void navigateToToken(final Token token) { | |
| 184 | if (counter++ > 6) { | 180 | navigateToToken(this, token, selectionHighlightPainter); |
| 185 | Timer timer = (Timer) event.getSource(); | 181 | } |
| 186 | timer.stop(); | 182 | |
| 187 | } | 183 | public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) { |
| 188 | } | 184 | for (Token token : tokens) { |
| 189 | }); | 185 | setHighlightedToken(token, painter); |
| 190 | timer.start(); | 186 | } |
| 191 | } | 187 | } |
| 192 | 188 | ||
| 193 | public void setHighlightedTokens(Iterable<Token> tokens, HighlightPainter painter) { | 189 | public void setHighlightedToken(Token token, HighlightPainter painter) { |
| 194 | for (Token token : tokens) { | 190 | try { |
| 195 | setHighlightedToken(token, painter); | 191 | getHighlighter().addHighlight(token.start, token.end, painter); |
| 196 | } | 192 | } catch (BadLocationException ex) { |
| 197 | } | 193 | throw new IllegalArgumentException(ex); |
| 198 | 194 | } | |
| 199 | public void setHighlightedToken(Token token, HighlightPainter painter) { | 195 | } |
| 200 | try { | 196 | |
| 201 | getHighlighter().addHighlight(token.start, token.end, painter); | 197 | public void clearHighlights() { |
| 202 | } catch (BadLocationException ex) { | 198 | getHighlighter().removeAllHighlights(); |
| 203 | throw new IllegalArgumentException(ex); | 199 | } |
| 204 | } | 200 | |
| 205 | } | 201 | public interface SelectionListener { |
| 206 | 202 | void onSelect(EntryReference<Entry, Entry> reference); | |
| 207 | public void clearHighlights() { | 203 | } |
| 208 | getHighlighter().removeAllHighlights(); | ||
| 209 | } | ||
| 210 | } | 204 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index 7cb494fa..9f8d6fc4 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -54,811 +55,790 @@ import java.util.function.Function; | |||
| 54 | 55 | ||
| 55 | public class Gui { | 56 | public class Gui { |
| 56 | 57 | ||
| 57 | private GuiController controller; | 58 | public final PopupMenuBar popupMenu; |
| 58 | 59 | private final PanelObf obfPanel; | |
| 59 | private final PanelObf obfPanel; | 60 | private final PanelDeobf deobfPanel; |
| 60 | private final PanelDeobf deobfPanel; | 61 | |
| 61 | 62 | private final MenuBar menuBar; | |
| 62 | private final MenuBar menuBar; | 63 | // state |
| 63 | public final PopupMenuBar popupMenu; | 64 | public EntryReference<Entry, Entry> reference; |
| 64 | 65 | public JFileChooser jarFileChooser; | |
| 65 | private JFrame frame; | 66 | public JFileChooser enigmaMappingsFileChooser; |
| 66 | private PanelEditor editor; | 67 | public JFileChooser exportSourceFileChooser; |
| 67 | private JPanel classesPanel; | 68 | public JFileChooser exportJarFileChooser; |
| 68 | private JSplitPane splitClasses; | 69 | private GuiController controller; |
| 69 | private PanelIdentifier infoPanel; | 70 | private JFrame frame; |
| 70 | private ObfuscatedHighlightPainter obfuscatedHighlightPainter; | 71 | private PanelEditor editor; |
| 71 | private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; | 72 | private JPanel classesPanel; |
| 72 | private OtherHighlightPainter otherHighlightPainter; | 73 | private JSplitPane splitClasses; |
| 73 | private SelectionHighlightPainter selectionHighlightPainter; | 74 | private PanelIdentifier infoPanel; |
| 74 | private JTree inheritanceTree; | 75 | private ObfuscatedHighlightPainter obfuscatedHighlightPainter; |
| 75 | private JTree implementationsTree; | 76 | private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; |
| 76 | private JTree callsTree; | 77 | private OtherHighlightPainter otherHighlightPainter; |
| 77 | private JList<Token> tokens; | 78 | private SelectionHighlightPainter selectionHighlightPainter; |
| 78 | private JTabbedPane tabs; | 79 | private JTree inheritanceTree; |
| 79 | 80 | private JTree implementationsTree; | |
| 80 | // state | 81 | private JTree callsTree; |
| 81 | public EntryReference<Entry, Entry> reference; | 82 | private JList<Token> tokens; |
| 82 | 83 | private JTabbedPane tabs; | |
| 83 | public JFileChooser jarFileChooser; | 84 | |
| 84 | public JFileChooser enigmaMappingsFileChooser; | 85 | public Gui() { |
| 85 | 86 | ||
| 86 | public JFileChooser exportSourceFileChooser; | 87 | // init frame |
| 87 | public JFileChooser exportJarFileChooser; | 88 | this.frame = new JFrame(Constants.NAME); |
| 88 | 89 | final Container pane = this.frame.getContentPane(); | |
| 89 | public Gui() { | 90 | pane.setLayout(new BorderLayout()); |
| 90 | 91 | ||
| 91 | // init frame | 92 | if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { |
| 92 | this.frame = new JFrame(Constants.NAME); | 93 | // install a global exception handler to the event thread |
| 93 | final Container pane = this.frame.getContentPane(); | 94 | CrashDialog.init(this.frame); |
| 94 | pane.setLayout(new BorderLayout()); | 95 | Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { |
| 95 | 96 | t.printStackTrace(System.err); | |
| 96 | if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { | 97 | if (!ExceptionIgnorer.shouldIgnore(t)) { |
| 97 | // install a global exception handler to the event thread | 98 | CrashDialog.show(t); |
| 98 | CrashDialog.init(this.frame); | 99 | } |
| 99 | Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { | 100 | }); |
| 100 | t.printStackTrace(System.err); | 101 | } |
| 101 | if (!ExceptionIgnorer.shouldIgnore(t)) { | 102 | |
| 102 | CrashDialog.show(t); | 103 | this.controller = new GuiController(this); |
| 103 | } | 104 | |
| 104 | }); | 105 | // init file choosers |
| 105 | } | 106 | this.jarFileChooser = new FileChooserFile(); |
| 106 | 107 | ||
| 107 | this.controller = new GuiController(this); | 108 | this.enigmaMappingsFileChooser = new FileChooserAny(); |
| 108 | 109 | this.exportSourceFileChooser = new FileChooserFolder(); | |
| 109 | // init file choosers | 110 | this.exportJarFileChooser = new FileChooserFile(); |
| 110 | this.jarFileChooser = new FileChooserFile(); | 111 | |
| 111 | 112 | this.obfPanel = new PanelObf(this); | |
| 112 | 113 | this.deobfPanel = new PanelDeobf(this); | |
| 113 | this.enigmaMappingsFileChooser = new FileChooserAny(); | 114 | |
| 114 | this.exportSourceFileChooser = new FileChooserFolder(); | 115 | // set up classes panel (don't add the splitter yet) |
| 115 | this.exportJarFileChooser = new FileChooserFile(); | 116 | splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); |
| 116 | 117 | splitClasses.setResizeWeight(0.3); | |
| 117 | this.obfPanel = new PanelObf(this); | 118 | this.classesPanel = new JPanel(); |
| 118 | this.deobfPanel = new PanelDeobf(this); | 119 | this.classesPanel.setLayout(new BorderLayout()); |
| 119 | 120 | this.classesPanel.setPreferredSize(new Dimension(250, 0)); | |
| 120 | // set up classes panel (don't add the splitter yet) | 121 | |
| 121 | splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); | 122 | // init info panel |
| 122 | splitClasses.setResizeWeight(0.3); | 123 | infoPanel = new PanelIdentifier(this); |
| 123 | this.classesPanel = new JPanel(); | 124 | infoPanel.clearReference(); |
| 124 | this.classesPanel.setLayout(new BorderLayout()); | 125 | |
| 125 | this.classesPanel.setPreferredSize(new Dimension(250, 0)); | 126 | // init editor |
| 126 | 127 | DefaultSyntaxKit.initKit(); | |
| 127 | // init info panel | 128 | obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); |
| 128 | infoPanel = new PanelIdentifier(this); | 129 | deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); |
| 129 | infoPanel.clearReference(); | 130 | otherHighlightPainter = new OtherHighlightPainter(); |
| 130 | 131 | selectionHighlightPainter = new SelectionHighlightPainter(); | |
| 131 | // init editor | 132 | this.editor = new PanelEditor(this); |
| 132 | DefaultSyntaxKit.initKit(); | 133 | JScrollPane sourceScroller = new JScrollPane(this.editor); |
| 133 | obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); | 134 | this.editor.setContentType("text/java"); |
| 134 | deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); | 135 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); |
| 135 | otherHighlightPainter = new OtherHighlightPainter(); | 136 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); |
| 136 | selectionHighlightPainter = new SelectionHighlightPainter(); | 137 | |
| 137 | this.editor = new PanelEditor(this); | 138 | // init editor popup menu |
| 138 | JScrollPane sourceScroller = new JScrollPane(this.editor); | 139 | this.popupMenu = new PopupMenuBar(this); |
| 139 | this.editor.setContentType("text/java"); | 140 | this.editor.setComponentPopupMenu(this.popupMenu); |
| 140 | DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); | 141 | |
| 141 | kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); | 142 | // init inheritance panel |
| 142 | 143 | inheritanceTree = new JTree(); | |
| 143 | // init editor popup menu | 144 | inheritanceTree.setModel(null); |
| 144 | this.popupMenu = new PopupMenuBar(this); | 145 | inheritanceTree.addMouseListener(new MouseAdapter() { |
| 145 | this.editor.setComponentPopupMenu(this.popupMenu); | 146 | @Override |
| 146 | 147 | public void mouseClicked(MouseEvent event) { | |
| 147 | // init inheritance panel | 148 | if (event.getClickCount() == 2) { |
| 148 | inheritanceTree = new JTree(); | 149 | // get the selected node |
| 149 | inheritanceTree.setModel(null); | 150 | TreePath path = inheritanceTree.getSelectionPath(); |
| 150 | inheritanceTree.addMouseListener(new MouseAdapter() { | 151 | if (path == null) { |
| 151 | @Override | 152 | return; |
| 152 | public void mouseClicked(MouseEvent event) { | 153 | } |
| 153 | if (event.getClickCount() == 2) { | 154 | |
| 154 | // get the selected node | 155 | Object node = path.getLastPathComponent(); |
| 155 | TreePath path = inheritanceTree.getSelectionPath(); | 156 | if (node instanceof ClassInheritanceTreeNode) { |
| 156 | if (path == null) { | 157 | ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; |
| 157 | return; | 158 | navigateTo(new ClassEntry(classNode.getObfClassName())); |
| 158 | } | 159 | } else if (node instanceof MethodInheritanceTreeNode) { |
| 159 | 160 | MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; | |
| 160 | Object node = path.getLastPathComponent(); | 161 | if (methodNode.isImplemented()) { |
| 161 | if (node instanceof ClassInheritanceTreeNode) { | 162 | navigateTo(methodNode.getMethodEntry()); |
| 162 | ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; | 163 | } |
| 163 | navigateTo(new ClassEntry(classNode.getObfClassName())); | 164 | } |
| 164 | } else if (node instanceof MethodInheritanceTreeNode) { | 165 | } |
| 165 | MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; | 166 | } |
| 166 | if (methodNode.isImplemented()) { | 167 | }); |
| 167 | navigateTo(methodNode.getMethodEntry()); | 168 | JPanel inheritancePanel = new JPanel(); |
| 168 | } | 169 | inheritancePanel.setLayout(new BorderLayout()); |
| 169 | } | 170 | inheritancePanel.add(new JScrollPane(inheritanceTree)); |
| 170 | } | 171 | |
| 171 | } | 172 | // init implementations panel |
| 172 | }); | 173 | implementationsTree = new JTree(); |
| 173 | JPanel inheritancePanel = new JPanel(); | 174 | implementationsTree.setModel(null); |
| 174 | inheritancePanel.setLayout(new BorderLayout()); | 175 | implementationsTree.addMouseListener(new MouseAdapter() { |
| 175 | inheritancePanel.add(new JScrollPane(inheritanceTree)); | 176 | @Override |
| 176 | 177 | public void mouseClicked(MouseEvent event) { | |
| 177 | // init implementations panel | 178 | if (event.getClickCount() == 2) { |
| 178 | implementationsTree = new JTree(); | 179 | // get the selected node |
| 179 | implementationsTree.setModel(null); | 180 | TreePath path = implementationsTree.getSelectionPath(); |
| 180 | implementationsTree.addMouseListener(new MouseAdapter() { | 181 | if (path == null) { |
| 181 | @Override | 182 | return; |
| 182 | public void mouseClicked(MouseEvent event) { | 183 | } |
| 183 | if (event.getClickCount() == 2) { | 184 | |
| 184 | // get the selected node | 185 | Object node = path.getLastPathComponent(); |
| 185 | TreePath path = implementationsTree.getSelectionPath(); | 186 | if (node instanceof ClassImplementationsTreeNode) { |
| 186 | if (path == null) { | 187 | ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; |
| 187 | return; | 188 | navigateTo(classNode.getClassEntry()); |
| 188 | } | 189 | } else if (node instanceof MethodImplementationsTreeNode) { |
| 189 | 190 | MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; | |
| 190 | Object node = path.getLastPathComponent(); | 191 | navigateTo(methodNode.getMethodEntry()); |
| 191 | if (node instanceof ClassImplementationsTreeNode) { | 192 | } |
| 192 | ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; | 193 | } |
| 193 | navigateTo(classNode.getClassEntry()); | 194 | } |
| 194 | } else if (node instanceof MethodImplementationsTreeNode) { | 195 | }); |
| 195 | MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; | 196 | JPanel implementationsPanel = new JPanel(); |
| 196 | navigateTo(methodNode.getMethodEntry()); | 197 | implementationsPanel.setLayout(new BorderLayout()); |
| 197 | } | 198 | implementationsPanel.add(new JScrollPane(implementationsTree)); |
| 198 | } | 199 | |
| 199 | } | 200 | // init call panel |
| 200 | }); | 201 | callsTree = new JTree(); |
| 201 | JPanel implementationsPanel = new JPanel(); | 202 | callsTree.setModel(null); |
| 202 | implementationsPanel.setLayout(new BorderLayout()); | 203 | callsTree.addMouseListener(new MouseAdapter() { |
| 203 | implementationsPanel.add(new JScrollPane(implementationsTree)); | 204 | @SuppressWarnings("unchecked") |
| 204 | 205 | @Override | |
| 205 | // init call panel | 206 | public void mouseClicked(MouseEvent event) { |
| 206 | callsTree = new JTree(); | 207 | if (event.getClickCount() == 2) { |
| 207 | callsTree.setModel(null); | 208 | // get the selected node |
| 208 | callsTree.addMouseListener(new MouseAdapter() { | 209 | TreePath path = callsTree.getSelectionPath(); |
| 209 | @SuppressWarnings("unchecked") | 210 | if (path == null) { |
| 210 | @Override | 211 | return; |
| 211 | public void mouseClicked(MouseEvent event) { | 212 | } |
| 212 | if (event.getClickCount() == 2) { | 213 | |
| 213 | // get the selected node | 214 | Object node = path.getLastPathComponent(); |
| 214 | TreePath path = callsTree.getSelectionPath(); | 215 | if (node instanceof ReferenceTreeNode) { |
| 215 | if (path == null) { | 216 | ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); |
| 216 | return; | 217 | if (referenceNode.getReference() != null) { |
| 217 | } | 218 | navigateTo(referenceNode.getReference()); |
| 218 | 219 | } else { | |
| 219 | Object node = path.getLastPathComponent(); | 220 | navigateTo(referenceNode.getEntry()); |
| 220 | if (node instanceof ReferenceTreeNode) { | 221 | } |
| 221 | ReferenceTreeNode<Entry, Entry> referenceNode = ((ReferenceTreeNode<Entry, Entry>) node); | 222 | } |
| 222 | if (referenceNode.getReference() != null) { | 223 | } |
| 223 | navigateTo(referenceNode.getReference()); | 224 | } |
| 224 | } else { | 225 | }); |
| 225 | navigateTo(referenceNode.getEntry()); | 226 | tokens = new JList<>(); |
| 226 | } | 227 | tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); |
| 227 | } | 228 | tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| 228 | } | 229 | tokens.setLayoutOrientation(JList.VERTICAL); |
| 229 | } | 230 | tokens.addMouseListener(new MouseAdapter() { |
| 230 | }); | 231 | @Override |
| 231 | tokens = new JList<>(); | 232 | public void mouseClicked(MouseEvent event) { |
| 232 | tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); | 233 | if (event.getClickCount() == 2) { |
| 233 | tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | 234 | Token selected = tokens.getSelectedValue(); |
| 234 | tokens.setLayoutOrientation(JList.VERTICAL); | 235 | if (selected != null) { |
| 235 | tokens.addMouseListener(new MouseAdapter() { | 236 | showToken(selected); |
| 236 | @Override | 237 | } |
| 237 | public void mouseClicked(MouseEvent event) { | 238 | } |
| 238 | if (event.getClickCount() == 2) { | 239 | } |
| 239 | Token selected = tokens.getSelectedValue(); | 240 | }); |
| 240 | if (selected != null) { | 241 | tokens.setPreferredSize(new Dimension(0, 200)); |
| 241 | showToken(selected); | 242 | tokens.setMinimumSize(new Dimension(0, 200)); |
| 242 | } | 243 | JSplitPane callPanel = new JSplitPane( |
| 243 | } | 244 | JSplitPane.VERTICAL_SPLIT, |
| 244 | } | 245 | true, |
| 245 | }); | 246 | new JScrollPane(callsTree), |
| 246 | tokens.setPreferredSize(new Dimension(0, 200)); | 247 | new JScrollPane(tokens) |
| 247 | tokens.setMinimumSize(new Dimension(0, 200)); | 248 | ); |
| 248 | JSplitPane callPanel = new JSplitPane( | 249 | callPanel.setResizeWeight(1); // let the top side take all the slack |
| 249 | JSplitPane.VERTICAL_SPLIT, | 250 | callPanel.resetToPreferredSizes(); |
| 250 | true, | 251 | |
| 251 | new JScrollPane(callsTree), | 252 | // layout controls |
| 252 | new JScrollPane(tokens) | 253 | JPanel centerPanel = new JPanel(); |
| 253 | ); | 254 | centerPanel.setLayout(new BorderLayout()); |
| 254 | callPanel.setResizeWeight(1); // let the top side take all the slack | 255 | centerPanel.add(infoPanel, BorderLayout.NORTH); |
| 255 | callPanel.resetToPreferredSizes(); | 256 | centerPanel.add(sourceScroller, BorderLayout.CENTER); |
| 256 | 257 | tabs = new JTabbedPane(); | |
| 257 | // layout controls | 258 | tabs.setPreferredSize(new Dimension(250, 0)); |
| 258 | JPanel centerPanel = new JPanel(); | 259 | tabs.addTab("Inheritance", inheritancePanel); |
| 259 | centerPanel.setLayout(new BorderLayout()); | 260 | tabs.addTab("Implementations", implementationsPanel); |
| 260 | centerPanel.add(infoPanel, BorderLayout.NORTH); | 261 | tabs.addTab("Call Graph", callPanel); |
| 261 | centerPanel.add(sourceScroller, BorderLayout.CENTER); | 262 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); |
| 262 | tabs = new JTabbedPane(); | 263 | splitRight.setResizeWeight(1); // let the left side take all the slack |
| 263 | tabs.setPreferredSize(new Dimension(250, 0)); | 264 | splitRight.resetToPreferredSizes(); |
| 264 | tabs.addTab("Inheritance", inheritancePanel); | 265 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); |
| 265 | tabs.addTab("Implementations", implementationsPanel); | 266 | splitCenter.setResizeWeight(0); // let the right side take all the slack |
| 266 | tabs.addTab("Call Graph", callPanel); | 267 | pane.add(splitCenter, BorderLayout.CENTER); |
| 267 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); | 268 | |
| 268 | splitRight.setResizeWeight(1); // let the left side take all the slack | 269 | // init menus |
| 269 | splitRight.resetToPreferredSizes(); | 270 | this.menuBar = new MenuBar(this); |
| 270 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); | 271 | this.frame.setJMenuBar(this.menuBar); |
| 271 | splitCenter.setResizeWeight(0); // let the right side take all the slack | 272 | |
| 272 | pane.add(splitCenter, BorderLayout.CENTER); | 273 | // init state |
| 273 | 274 | onCloseJar(); | |
| 274 | // init menus | 275 | |
| 275 | this.menuBar = new MenuBar(this); | 276 | this.frame.addWindowListener(new WindowAdapter() { |
| 276 | this.frame.setJMenuBar(this.menuBar); | 277 | @Override |
| 277 | 278 | public void windowClosing(WindowEvent event) { | |
| 278 | // init state | 279 | close(); |
| 279 | onCloseJar(); | 280 | } |
| 280 | 281 | }); | |
| 281 | this.frame.addWindowListener(new WindowAdapter() { | 282 | |
| 282 | @Override | 283 | // show the frame |
| 283 | public void windowClosing(WindowEvent event) { | 284 | pane.doLayout(); |
| 284 | close(); | 285 | this.frame.setSize(1024, 576); |
| 285 | } | 286 | this.frame.setMinimumSize(new Dimension(640, 480)); |
| 286 | }); | 287 | this.frame.setVisible(true); |
| 287 | 288 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | |
| 288 | // show the frame | 289 | } |
| 289 | pane.doLayout(); | 290 | |
| 290 | this.frame.setSize(1024, 576); | 291 | public JFrame getFrame() { |
| 291 | this.frame.setMinimumSize(new Dimension(640, 480)); | 292 | return this.frame; |
| 292 | this.frame.setVisible(true); | 293 | } |
| 293 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 294 | |
| 294 | } | 295 | public GuiController getController() { |
| 295 | 296 | return this.controller; | |
| 296 | public JFrame getFrame() { | 297 | } |
| 297 | return this.frame; | 298 | |
| 298 | } | 299 | public void onStartOpenJar() { |
| 299 | 300 | this.classesPanel.removeAll(); | |
| 300 | public GuiController getController() { | 301 | JPanel panel = new JPanel(); |
| 301 | return this.controller; | 302 | panel.setLayout(new FlowLayout()); |
| 302 | } | 303 | panel.add(new JLabel("Loading...")); |
| 303 | 304 | this.classesPanel.add(panel); | |
| 304 | public void onStartOpenJar() { | 305 | redraw(); |
| 305 | this.classesPanel.removeAll(); | 306 | } |
| 306 | JPanel panel = new JPanel(); | 307 | |
| 307 | panel.setLayout(new FlowLayout()); | 308 | public void onFinishOpenJar(String jarName) { |
| 308 | panel.add(new JLabel("Loading...")); | 309 | // update gui |
| 309 | this.classesPanel.add(panel); | 310 | this.frame.setTitle(Constants.NAME + " - " + jarName); |
| 310 | redraw(); | 311 | this.classesPanel.removeAll(); |
| 311 | } | 312 | this.classesPanel.add(splitClasses); |
| 312 | 313 | setSource(null); | |
| 313 | public void onFinishOpenJar(String jarName) { | 314 | |
| 314 | // update gui | 315 | // update menu |
| 315 | this.frame.setTitle(Constants.NAME + " - " + jarName); | 316 | this.menuBar.closeJarMenu.setEnabled(true); |
| 316 | this.classesPanel.removeAll(); | 317 | this.menuBar.openEnigmaMappingsMenu.setEnabled(true); |
| 317 | this.classesPanel.add(splitClasses); | 318 | this.menuBar.saveMappingsMenu.setEnabled(false); |
| 318 | setSource(null); | 319 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); |
| 319 | 320 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); | |
| 320 | // update menu | 321 | this.menuBar.saveMappingsSrgMenu.setEnabled(true); |
| 321 | this.menuBar.closeJarMenu.setEnabled(true); | 322 | this.menuBar.closeMappingsMenu.setEnabled(true); |
| 322 | this.menuBar.openEnigmaMappingsMenu.setEnabled(true); | 323 | this.menuBar.exportSourceMenu.setEnabled(true); |
| 323 | this.menuBar.saveMappingsMenu.setEnabled(false); | 324 | this.menuBar.exportJarMenu.setEnabled(true); |
| 324 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); | 325 | |
| 325 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); | 326 | redraw(); |
| 326 | this.menuBar.saveMappingsSrgMenu.setEnabled(true); | 327 | } |
| 327 | this.menuBar.closeMappingsMenu.setEnabled(true); | 328 | |
| 328 | this.menuBar.exportSourceMenu.setEnabled(true); | 329 | public void onCloseJar() { |
| 329 | this.menuBar.exportJarMenu.setEnabled(true); | 330 | // update gui |
| 330 | 331 | this.frame.setTitle(Constants.NAME); | |
| 331 | redraw(); | 332 | setObfClasses(null); |
| 332 | } | 333 | setDeobfClasses(null); |
| 333 | 334 | setSource(null); | |
| 334 | public void onCloseJar() { | 335 | this.classesPanel.removeAll(); |
| 335 | // update gui | 336 | |
| 336 | this.frame.setTitle(Constants.NAME); | 337 | // update menu |
| 337 | setObfClasses(null); | 338 | this.menuBar.closeJarMenu.setEnabled(false); |
| 338 | setDeobfClasses(null); | 339 | this.menuBar.openEnigmaMappingsMenu.setEnabled(false); |
| 339 | setSource(null); | 340 | this.menuBar.saveMappingsMenu.setEnabled(false); |
| 340 | this.classesPanel.removeAll(); | 341 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); |
| 341 | 342 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); | |
| 342 | // update menu | 343 | this.menuBar.saveMappingsSrgMenu.setEnabled(false); |
| 343 | this.menuBar.closeJarMenu.setEnabled(false); | 344 | this.menuBar.closeMappingsMenu.setEnabled(false); |
| 344 | this.menuBar.openEnigmaMappingsMenu.setEnabled(false); | 345 | this.menuBar.exportSourceMenu.setEnabled(false); |
| 345 | this.menuBar.saveMappingsMenu.setEnabled(false); | 346 | this.menuBar.exportJarMenu.setEnabled(false); |
| 346 | this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); | 347 | |
| 347 | this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); | 348 | redraw(); |
| 348 | this.menuBar.saveMappingsSrgMenu.setEnabled(false); | 349 | } |
| 349 | this.menuBar.closeMappingsMenu.setEnabled(false); | 350 | |
| 350 | this.menuBar.exportSourceMenu.setEnabled(false); | 351 | public void setObfClasses(Collection<ClassEntry> obfClasses) { |
| 351 | this.menuBar.exportJarMenu.setEnabled(false); | 352 | this.obfPanel.obfClasses.setClasses(obfClasses); |
| 352 | 353 | } | |
| 353 | redraw(); | 354 | |
| 354 | } | 355 | public void setDeobfClasses(Collection<ClassEntry> deobfClasses) { |
| 355 | 356 | this.deobfPanel.deobfClasses.setClasses(deobfClasses); | |
| 356 | public void setObfClasses(Collection<ClassEntry> obfClasses) { | 357 | } |
| 357 | this.obfPanel.obfClasses.setClasses(obfClasses); | 358 | |
| 358 | } | 359 | public void setMappingsFile(File file) { |
| 359 | 360 | this.enigmaMappingsFileChooser.setSelectedFile(file); | |
| 360 | public void setDeobfClasses(Collection<ClassEntry> deobfClasses) { | 361 | this.menuBar.saveMappingsMenu.setEnabled(file != null); |
| 361 | this.deobfPanel.deobfClasses.setClasses(deobfClasses); | 362 | } |
| 362 | } | 363 | |
| 363 | 364 | public void setSource(String source) { | |
| 364 | public void setMappingsFile(File file) { | 365 | this.editor.getHighlighter().removeAllHighlights(); |
| 365 | this.enigmaMappingsFileChooser.setSelectedFile(file); | 366 | this.editor.setText(source); |
| 366 | this.menuBar.saveMappingsMenu.setEnabled(file != null); | 367 | } |
| 367 | } | 368 | |
| 368 | 369 | public void showToken(final Token token) { | |
| 369 | public void setSource(String source) { | 370 | if (token == null) { |
| 370 | this.editor.getHighlighter().removeAllHighlights(); | 371 | throw new IllegalArgumentException("Token cannot be null!"); |
| 371 | this.editor.setText(source); | 372 | } |
| 372 | } | 373 | CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); |
| 373 | 374 | redraw(); | |
| 374 | public void showToken(final Token token) { | 375 | } |
| 375 | if (token == null) { | 376 | |
| 376 | throw new IllegalArgumentException("Token cannot be null!"); | 377 | public void showTokens(Collection<Token> tokens) { |
| 377 | } | 378 | Vector<Token> sortedTokens = new Vector<>(tokens); |
| 378 | CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); | 379 | Collections.sort(sortedTokens); |
| 379 | redraw(); | 380 | if (sortedTokens.size() > 1) { |
| 380 | } | 381 | // sort the tokens and update the tokens panel |
| 381 | 382 | this.tokens.setListData(sortedTokens); | |
| 382 | public void showTokens(Collection<Token> tokens) { | 383 | this.tokens.setSelectedIndex(0); |
| 383 | Vector<Token> sortedTokens = new Vector<>(tokens); | 384 | } else { |
| 384 | Collections.sort(sortedTokens); | 385 | this.tokens.setListData(new Vector<>()); |
| 385 | if (sortedTokens.size() > 1) { | 386 | } |
| 386 | // sort the tokens and update the tokens panel | 387 | |
| 387 | this.tokens.setListData(sortedTokens); | 388 | // show the first token |
| 388 | this.tokens.setSelectedIndex(0); | 389 | showToken(sortedTokens.get(0)); |
| 389 | } else { | 390 | } |
| 390 | this.tokens.setListData(new Vector<>()); | 391 | |
| 391 | } | 392 | public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) { |
| 392 | 393 | ||
| 393 | // show the first token | 394 | // remove any old highlighters |
| 394 | showToken(sortedTokens.get(0)); | 395 | this.editor.getHighlighter().removeAllHighlights(); |
| 395 | } | 396 | |
| 396 | 397 | // color things based on the index | |
| 397 | public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) { | 398 | if (obfuscatedTokens != null) { |
| 398 | 399 | setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); | |
| 399 | // remove any old highlighters | 400 | } |
| 400 | this.editor.getHighlighter().removeAllHighlights(); | 401 | if (deobfuscatedTokens != null) { |
| 401 | 402 | setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); | |
| 402 | // color things based on the index | 403 | } |
| 403 | if (obfuscatedTokens != null) { | 404 | if (otherTokens != null) { |
| 404 | setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); | 405 | setHighlightedTokens(otherTokens, otherHighlightPainter); |
| 405 | } | 406 | } |
| 406 | if (deobfuscatedTokens != null) { | 407 | |
| 407 | setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); | 408 | redraw(); |
| 408 | } | 409 | } |
| 409 | if (otherTokens != null) { | 410 | |
| 410 | setHighlightedTokens(otherTokens, otherHighlightPainter); | 411 | private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) { |
| 411 | } | 412 | for (Token token : tokens) { |
| 412 | 413 | try { | |
| 413 | redraw(); | 414 | this.editor.getHighlighter().addHighlight(token.start, token.end, painter); |
| 414 | } | 415 | } catch (BadLocationException ex) { |
| 415 | 416 | throw new IllegalArgumentException(ex); | |
| 416 | private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) { | 417 | } |
| 417 | for (Token token : tokens) { | 418 | } |
| 418 | try { | 419 | } |
| 419 | this.editor.getHighlighter().addHighlight(token.start, token.end, painter); | 420 | |
| 420 | } catch (BadLocationException ex) { | 421 | private void showReference(EntryReference<Entry, Entry> reference) { |
| 421 | throw new IllegalArgumentException(ex); | 422 | if (reference == null) { |
| 422 | } | 423 | infoPanel.clearReference(); |
| 423 | } | 424 | return; |
| 424 | } | 425 | } |
| 425 | 426 | ||
| 426 | private void showReference(EntryReference<Entry, Entry> reference) { | 427 | this.reference = reference; |
| 427 | if (reference == null) { | 428 | |
| 428 | infoPanel.clearReference(); | 429 | infoPanel.removeAll(); |
| 429 | return; | 430 | if (reference.entry instanceof ClassEntry) { |
| 430 | } | 431 | showClassEntry((ClassEntry) this.reference.entry); |
| 431 | 432 | } else if (this.reference.entry instanceof FieldEntry) { | |
| 432 | this.reference = reference; | 433 | showFieldEntry((FieldEntry) this.reference.entry); |
| 433 | 434 | } else if (this.reference.entry instanceof MethodEntry) { | |
| 434 | infoPanel.removeAll(); | 435 | showMethodEntry((MethodEntry) this.reference.entry); |
| 435 | if (reference.entry instanceof ClassEntry) { | 436 | } else if (this.reference.entry instanceof ConstructorEntry) { |
| 436 | showClassEntry((ClassEntry) this.reference.entry); | 437 | showConstructorEntry((ConstructorEntry) this.reference.entry); |
| 437 | } else if (this.reference.entry instanceof FieldEntry) { | 438 | } else if (this.reference.entry instanceof ArgumentEntry) { |
| 438 | showFieldEntry((FieldEntry) this.reference.entry); | 439 | showArgumentEntry((ArgumentEntry) this.reference.entry); |
| 439 | } else if (this.reference.entry instanceof MethodEntry) { | 440 | } else if (this.reference.entry instanceof LocalVariableEntry) { |
| 440 | showMethodEntry((MethodEntry) this.reference.entry); | 441 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); |
| 441 | } else if (this.reference.entry instanceof ConstructorEntry) { | 442 | } else { |
| 442 | showConstructorEntry((ConstructorEntry) this.reference.entry); | 443 | throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); |
| 443 | } else if (this.reference.entry instanceof ArgumentEntry) { | 444 | } |
| 444 | showArgumentEntry((ArgumentEntry) this.reference.entry); | 445 | |
| 445 | } else if (this.reference.entry instanceof LocalVariableEntry) { | 446 | redraw(); |
| 446 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); | 447 | } |
| 447 | } else { | 448 | |
| 448 | throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); | 449 | private void showLocalVariableEntry(LocalVariableEntry entry) { |
| 449 | } | 450 | addNameValue(infoPanel, "Variable", entry.getName()); |
| 450 | 451 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | |
| 451 | redraw(); | 452 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); |
| 452 | } | 453 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); |
| 453 | 454 | addNameValue(infoPanel, "Type", entry.getType().toString()); | |
| 454 | private void showLocalVariableEntry(LocalVariableEntry entry) { | 455 | } |
| 455 | addNameValue(infoPanel, "Variable", entry.getName()); | 456 | |
| 456 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 457 | private void showClassEntry(ClassEntry entry) { |
| 457 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); | 458 | addNameValue(infoPanel, "Class", entry.getName()); |
| 458 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); | 459 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 459 | addNameValue(infoPanel, "Type", entry.getType().toString()); | 460 | } |
| 460 | } | 461 | |
| 461 | 462 | private void showFieldEntry(FieldEntry entry) { | |
| 462 | private void showClassEntry(ClassEntry entry) { | 463 | addNameValue(infoPanel, "Field", entry.getName()); |
| 463 | addNameValue(infoPanel, "Class", entry.getName()); | 464 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 464 | addModifierComboBox(infoPanel, "Modifier", entry); | 465 | addNameValue(infoPanel, "Type", entry.getType().toString()); |
| 465 | } | 466 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 466 | 467 | } | |
| 467 | private void showFieldEntry(FieldEntry entry) { | 468 | |
| 468 | addNameValue(infoPanel, "Field", entry.getName()); | 469 | private void showMethodEntry(MethodEntry entry) { |
| 469 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 470 | addNameValue(infoPanel, "Method", entry.getName()); |
| 470 | addNameValue(infoPanel, "Type", entry.getType().toString()); | 471 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 471 | addModifierComboBox(infoPanel, "Modifier", entry); | 472 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); |
| 472 | } | 473 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 473 | 474 | ||
| 474 | private void showMethodEntry(MethodEntry entry) { | 475 | } |
| 475 | addNameValue(infoPanel, "Method", entry.getName()); | 476 | |
| 476 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 477 | private void showConstructorEntry(ConstructorEntry entry) { |
| 477 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | 478 | addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); |
| 478 | addModifierComboBox(infoPanel, "Modifier", entry); | 479 | if (!entry.isStatic()) { |
| 479 | 480 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | |
| 480 | } | 481 | addModifierComboBox(infoPanel, "Modifier", entry); |
| 481 | 482 | } | |
| 482 | private void showConstructorEntry(ConstructorEntry entry) { | 483 | } |
| 483 | addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); | 484 | |
| 484 | if (!entry.isStatic()) { | 485 | private void showArgumentEntry(ArgumentEntry entry) { |
| 485 | addNameValue(infoPanel, "Signature", entry.getSignature().toString()); | 486 | addNameValue(infoPanel, "Argument", entry.getName()); |
| 486 | addModifierComboBox(infoPanel, "Modifier", entry); | 487 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); |
| 487 | } | 488 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); |
| 488 | } | 489 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); |
| 489 | 490 | } | |
| 490 | private void showArgumentEntry(ArgumentEntry entry) { | 491 | |
| 491 | addNameValue(infoPanel, "Argument", entry.getName()); | 492 | private void addNameValue(JPanel container, String name, String value) { |
| 492 | addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); | 493 | JPanel panel = new JPanel(); |
| 493 | addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); | 494 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); |
| 494 | addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); | 495 | container.add(panel); |
| 495 | } | 496 | |
| 496 | 497 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | |
| 497 | private void addNameValue(JPanel container, String name, String value) { | 498 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 498 | JPanel panel = new JPanel(); | 499 | panel.add(label); |
| 499 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); | 500 | |
| 500 | container.add(panel); | 501 | panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); |
| 501 | 502 | } | |
| 502 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | 503 | |
| 503 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 504 | private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) { |
| 504 | panel.add(label); | 505 | if (!getController().entryIsInJar(entry)) |
| 505 | 506 | return null; | |
| 506 | panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); | 507 | JPanel panel = new JPanel(); |
| 507 | } | 508 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); |
| 508 | 509 | container.add(panel); | |
| 509 | private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry) | 510 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); |
| 510 | { | 511 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 511 | if (!getController().entryIsInJar(entry)) | 512 | panel.add(label); |
| 512 | return null; | 513 | JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); |
| 513 | JPanel panel = new JPanel(); | 514 | ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); |
| 514 | panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); | 515 | combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); |
| 515 | container.add(panel); | 516 | combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); |
| 516 | JLabel label = new JLabel(name + ":", JLabel.RIGHT); | 517 | combo.addItemListener(getController()::modifierChange); |
| 517 | label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 518 | panel.add(combo); |
| 518 | panel.add(label); | 519 | return combo; |
| 519 | JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values()); | 520 | } |
| 520 | ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); | 521 | |
| 521 | combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); | 522 | public void onCaretMove(int pos) { |
| 522 | combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); | 523 | |
| 523 | combo.addItemListener(getController()::modifierChange); | 524 | Token token = this.controller.getToken(pos); |
| 524 | panel.add(combo); | 525 | boolean isToken = token != null; |
| 525 | return combo; | 526 | |
| 526 | } | 527 | reference = this.controller.getDeobfReference(token); |
| 527 | 528 | boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; | |
| 528 | public void onCaretMove(int pos) { | 529 | boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; |
| 529 | 530 | boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; | |
| 530 | Token token = this.controller.getToken(pos); | 531 | boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; |
| 531 | boolean isToken = token != null; | 532 | boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); |
| 532 | 533 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | |
| 533 | reference = this.controller.getDeobfReference(token); | 534 | |
| 534 | boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; | 535 | if (isToken) { |
| 535 | boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; | 536 | showReference(reference); |
| 536 | boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; | 537 | } else { |
| 537 | boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; | 538 | infoPanel.clearReference(); |
| 538 | boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); | 539 | } |
| 539 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | 540 | |
| 540 | 541 | this.popupMenu.renameMenu.setEnabled(isRenameable); | |
| 541 | if (isToken) { | 542 | this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); |
| 542 | showReference(reference); | 543 | this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); |
| 543 | } else { | 544 | this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); |
| 544 | infoPanel.clearReference(); | 545 | this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); |
| 545 | } | 546 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); |
| 546 | 547 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | |
| 547 | this.popupMenu.renameMenu.setEnabled(isRenameable); | 548 | |
| 548 | this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); | 549 | if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { |
| 549 | this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); | 550 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); |
| 550 | this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); | 551 | } else { |
| 551 | this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); | 552 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); |
| 552 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); | 553 | } |
| 553 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | 554 | } |
| 554 | 555 | ||
| 555 | if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { | 556 | public void navigateTo(Entry entry) { |
| 556 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); | 557 | if (!this.controller.entryIsInJar(entry)) { |
| 557 | } else { | 558 | // entry is not in the jar. Ignore it |
| 558 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); | 559 | return; |
| 559 | } | 560 | } |
| 560 | } | 561 | if (reference != null) { |
| 561 | 562 | this.controller.savePreviousReference(reference); | |
| 562 | public void navigateTo(Entry entry) { | 563 | } |
| 563 | if (!this.controller.entryIsInJar(entry)) { | 564 | this.controller.openDeclaration(entry); |
| 564 | // entry is not in the jar. Ignore it | 565 | } |
| 565 | return; | 566 | |
| 566 | } | 567 | private void navigateTo(EntryReference<Entry, Entry> reference) { |
| 567 | if (reference != null) { | 568 | if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { |
| 568 | this.controller.savePreviousReference(reference); | 569 | return; |
| 569 | } | 570 | } |
| 570 | this.controller.openDeclaration(entry); | 571 | if (this.reference != null) { |
| 571 | } | 572 | this.controller.savePreviousReference(this.reference); |
| 572 | 573 | } | |
| 573 | private void navigateTo(EntryReference<Entry, Entry> reference) { | 574 | this.controller.openReference(reference); |
| 574 | if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { | 575 | } |
| 575 | return; | 576 | |
| 576 | } | 577 | public void startRename() { |
| 577 | if (this.reference != null) { | 578 | |
| 578 | this.controller.savePreviousReference(this.reference); | 579 | // init the text box |
| 579 | } | 580 | final JTextField text = new JTextField(); |
| 580 | this.controller.openReference(reference); | 581 | text.setText(reference.getNamableName()); |
| 581 | } | 582 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); |
| 582 | 583 | text.addKeyListener(new KeyAdapter() { | |
| 583 | public void startRename() { | 584 | @Override |
| 584 | 585 | public void keyPressed(KeyEvent event) { | |
| 585 | // init the text box | 586 | switch (event.getKeyCode()) { |
| 586 | final JTextField text = new JTextField(); | 587 | case KeyEvent.VK_ENTER: |
| 587 | text.setText(reference.getNamableName()); | 588 | finishRename(text, true); |
| 588 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); | 589 | break; |
| 589 | text.addKeyListener(new KeyAdapter() { | 590 | |
| 590 | @Override | 591 | case KeyEvent.VK_ESCAPE: |
| 591 | public void keyPressed(KeyEvent event) { | 592 | finishRename(text, false); |
| 592 | switch (event.getKeyCode()) { | 593 | break; |
| 593 | case KeyEvent.VK_ENTER: | 594 | default: |
| 594 | finishRename(text, true); | 595 | break; |
| 595 | break; | 596 | } |
| 596 | 597 | } | |
| 597 | case KeyEvent.VK_ESCAPE: | 598 | }); |
| 598 | finishRename(text, false); | 599 | |
| 599 | break; | 600 | // find the label with the name and replace it with the text box |
| 600 | default: | 601 | JPanel panel = (JPanel) infoPanel.getComponent(0); |
| 601 | break; | 602 | panel.remove(panel.getComponentCount() - 1); |
| 602 | } | 603 | panel.add(text); |
| 603 | } | 604 | text.grabFocus(); |
| 604 | }); | 605 | |
| 605 | 606 | int offset = text.getText().lastIndexOf('/') + 1; | |
| 606 | // find the label with the name and replace it with the text box | 607 | // If it's a class and isn't in the default package, assume that it's deobfuscated. |
| 607 | JPanel panel = (JPanel) infoPanel.getComponent(0); | 608 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) |
| 608 | panel.remove(panel.getComponentCount() - 1); | 609 | text.select(offset, text.getText().length()); |
| 609 | panel.add(text); | 610 | else |
| 610 | text.grabFocus(); | 611 | text.selectAll(); |
| 611 | 612 | ||
| 612 | int offset = text.getText().lastIndexOf('/') + 1; | 613 | redraw(); |
| 613 | // If it's a class and isn't in the default package, assume that it's deobfuscated. | 614 | } |
| 614 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) | 615 | |
| 615 | text.select(offset, text.getText().length()); | 616 | private void finishRename(JTextField text, boolean saveName) { |
| 616 | else | 617 | String newName = text.getText(); |
| 617 | text.selectAll(); | 618 | if (saveName && newName != null && !newName.isEmpty()) { |
| 618 | 619 | try { | |
| 619 | redraw(); | 620 | this.controller.rename(reference, newName); |
| 620 | } | 621 | } catch (IllegalNameException ex) { |
| 621 | 622 | text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); | |
| 622 | private void finishRename(JTextField text, boolean saveName) { | 623 | text.setToolTipText(ex.getReason()); |
| 623 | String newName = text.getText(); | 624 | Utils.showToolTipNow(text); |
| 624 | if (saveName && newName != null && newName.length() > 0) { | 625 | } |
| 625 | try { | 626 | return; |
| 626 | this.controller.rename(reference, newName); | 627 | } |
| 627 | } catch (IllegalNameException ex) { | 628 | |
| 628 | text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); | 629 | // abort the rename |
| 629 | text.setToolTipText(ex.getReason()); | 630 | JPanel panel = (JPanel) infoPanel.getComponent(0); |
| 630 | Utils.showToolTipNow(text); | 631 | panel.remove(panel.getComponentCount() - 1); |
| 631 | } | 632 | panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); |
| 632 | return; | 633 | |
| 633 | } | 634 | this.editor.grabFocus(); |
| 634 | 635 | ||
| 635 | // abort the rename | 636 | redraw(); |
| 636 | JPanel panel = (JPanel) infoPanel.getComponent(0); | 637 | } |
| 637 | panel.remove(panel.getComponentCount() - 1); | 638 | |
| 638 | panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); | 639 | public void showInheritance() { |
| 639 | 640 | ||
| 640 | this.editor.grabFocus(); | 641 | if (reference == null) { |
| 641 | 642 | return; | |
| 642 | redraw(); | 643 | } |
| 643 | } | 644 | |
| 644 | 645 | inheritanceTree.setModel(null); | |
| 645 | public void showInheritance() { | 646 | |
| 646 | 647 | if (reference.entry instanceof ClassEntry) { | |
| 647 | if (reference == null) { | 648 | // get the class inheritance |
| 648 | return; | 649 | ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); |
| 649 | } | 650 | |
| 650 | 651 | // show the tree at the root | |
| 651 | inheritanceTree.setModel(null); | 652 | TreePath path = getPathToRoot(classNode); |
| 652 | 653 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | |
| 653 | if (reference.entry instanceof ClassEntry) { | 654 | inheritanceTree.expandPath(path); |
| 654 | // get the class inheritance | 655 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); |
| 655 | ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); | 656 | } else if (reference.entry instanceof MethodEntry) { |
| 656 | 657 | // get the method inheritance | |
| 657 | // show the tree at the root | 658 | MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); |
| 658 | TreePath path = getPathToRoot(classNode); | 659 | |
| 659 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 660 | // show the tree at the root |
| 660 | inheritanceTree.expandPath(path); | 661 | TreePath path = getPathToRoot(classNode); |
| 661 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); | 662 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); |
| 662 | } else if (reference.entry instanceof MethodEntry) { | 663 | inheritanceTree.expandPath(path); |
| 663 | // get the method inheritance | 664 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); |
| 664 | MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); | 665 | } |
| 665 | 666 | ||
| 666 | // show the tree at the root | 667 | tabs.setSelectedIndex(0); |
| 667 | TreePath path = getPathToRoot(classNode); | 668 | redraw(); |
| 668 | inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 669 | } |
| 669 | inheritanceTree.expandPath(path); | 670 | |
| 670 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); | 671 | public void showImplementations() { |
| 671 | } | 672 | |
| 672 | 673 | if (reference == null) { | |
| 673 | tabs.setSelectedIndex(0); | 674 | return; |
| 674 | redraw(); | 675 | } |
| 675 | } | 676 | |
| 676 | 677 | implementationsTree.setModel(null); | |
| 677 | public void showImplementations() { | 678 | |
| 678 | 679 | DefaultMutableTreeNode node = null; | |
| 679 | if (reference == null) { | 680 | |
| 680 | return; | 681 | // get the class implementations |
| 681 | } | 682 | if (reference.entry instanceof ClassEntry) |
| 682 | 683 | node = this.controller.getClassImplementations((ClassEntry) reference.entry); | |
| 683 | implementationsTree.setModel(null); | 684 | else // get the method implementations |
| 684 | 685 | if (reference.entry instanceof MethodEntry) | |
| 685 | DefaultMutableTreeNode node = null; | 686 | node = this.controller.getMethodImplementations((MethodEntry) reference.entry); |
| 686 | 687 | ||
| 687 | // get the class implementations | 688 | if (node != null) { |
| 688 | if (reference.entry instanceof ClassEntry) | 689 | // show the tree at the root |
| 689 | node = this.controller.getClassImplementations((ClassEntry) reference.entry); | 690 | TreePath path = getPathToRoot(node); |
| 690 | else // get the method implementations | 691 | implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); |
| 691 | if (reference.entry instanceof MethodEntry) | 692 | implementationsTree.expandPath(path); |
| 692 | node = this.controller.getMethodImplementations((MethodEntry) reference.entry); | 693 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); |
| 693 | 694 | } | |
| 694 | if (node != null) { | 695 | |
| 695 | // show the tree at the root | 696 | tabs.setSelectedIndex(1); |
| 696 | TreePath path = getPathToRoot(node); | 697 | redraw(); |
| 697 | implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | 698 | } |
| 698 | implementationsTree.expandPath(path); | 699 | |
| 699 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); | 700 | public void showCalls() { |
| 700 | } | 701 | if (reference == null) { |
| 701 | 702 | return; | |
| 702 | tabs.setSelectedIndex(1); | 703 | } |
| 703 | redraw(); | 704 | |
| 704 | } | 705 | if (reference.entry instanceof ClassEntry) { |
| 705 | 706 | // look for calls to the default constructor | |
| 706 | public void showCalls() { | 707 | // TODO: get a list of all the constructors and find calls to all of them |
| 707 | if (reference == null) { | 708 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); |
| 708 | return; | 709 | callsTree.setModel(new DefaultTreeModel(node)); |
| 709 | } | 710 | } else if (reference.entry instanceof FieldEntry) { |
| 710 | 711 | FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); | |
| 711 | if (reference.entry instanceof ClassEntry) { | 712 | callsTree.setModel(new DefaultTreeModel(node)); |
| 712 | // look for calls to the default constructor | 713 | } else if (reference.entry instanceof MethodEntry) { |
| 713 | // TODO: get a list of all the constructors and find calls to all of them | 714 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); |
| 714 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); | 715 | callsTree.setModel(new DefaultTreeModel(node)); |
| 715 | callsTree.setModel(new DefaultTreeModel(node)); | 716 | } else if (reference.entry instanceof ConstructorEntry) { |
| 716 | } else if (reference.entry instanceof FieldEntry) { | 717 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); |
| 717 | FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); | 718 | callsTree.setModel(new DefaultTreeModel(node)); |
| 718 | callsTree.setModel(new DefaultTreeModel(node)); | 719 | } |
| 719 | } else if (reference.entry instanceof MethodEntry) { | 720 | |
| 720 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); | 721 | tabs.setSelectedIndex(2); |
| 721 | callsTree.setModel(new DefaultTreeModel(node)); | 722 | redraw(); |
| 722 | } else if (reference.entry instanceof ConstructorEntry) { | 723 | } |
| 723 | BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); | 724 | |
| 724 | callsTree.setModel(new DefaultTreeModel(node)); | 725 | public void toggleMapping() { |
| 725 | } | 726 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { |
| 726 | 727 | this.controller.removeMapping(reference); | |
| 727 | tabs.setSelectedIndex(2); | 728 | } else { |
| 728 | redraw(); | 729 | this.controller.markAsDeobfuscated(reference); |
| 729 | } | 730 | } |
| 730 | 731 | } | |
| 731 | public void toggleMapping() { | 732 | |
| 732 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { | 733 | private TreePath getPathToRoot(TreeNode node) { |
| 733 | this.controller.removeMapping(reference); | 734 | List<TreeNode> nodes = Lists.newArrayList(); |
| 734 | } else { | 735 | TreeNode n = node; |
| 735 | this.controller.markAsDeobfuscated(reference); | 736 | do { |
| 736 | } | 737 | nodes.add(n); |
| 737 | } | 738 | n = n.getParent(); |
| 738 | 739 | } while (n != null); | |
| 739 | private TreePath getPathToRoot(TreeNode node) { | 740 | Collections.reverse(nodes); |
| 740 | List<TreeNode> nodes = Lists.newArrayList(); | 741 | return new TreePath(nodes.toArray()); |
| 741 | TreeNode n = node; | 742 | } |
| 742 | do { | 743 | |
| 743 | nodes.add(n); | 744 | public void showDiscardDiag(Function<Integer, Void> callback, String... options) { |
| 744 | n = n.getParent(); | 745 | 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, |
| 745 | } while (n != null); | 746 | JOptionPane.QUESTION_MESSAGE, null, options, options[2]); |
| 746 | Collections.reverse(nodes); | 747 | callback.apply(response); |
| 747 | return new TreePath(nodes.toArray()); | 748 | } |
| 748 | } | 749 | |
| 749 | 750 | public void saveMapping() throws IOException { | |
| 750 | public void showDiscardDiag(Function<Integer, Void> callback, String... options) | 751 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) |
| 751 | { | 752 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); |
| 752 | 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, | 753 | } |
| 753 | JOptionPane.QUESTION_MESSAGE, null, options, options[2]); | 754 | |
| 754 | callback.apply(response); | 755 | public void close() { |
| 755 | } | 756 | if (!this.controller.isDirty()) { |
| 756 | 757 | // everything is saved, we can exit safely | |
| 757 | public void saveMapping() throws IOException | 758 | this.frame.dispose(); |
| 758 | { | 759 | System.exit(0); |
| 759 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) | 760 | } else { |
| 760 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); | 761 | // ask to save before closing |
| 761 | } | 762 | showDiscardDiag((response) -> { |
| 762 | 763 | if (response == JOptionPane.YES_OPTION) { | |
| 763 | public void close() { | 764 | try { |
| 764 | if (!this.controller.isDirty()) { | 765 | this.saveMapping(); |
| 765 | // everything is saved, we can exit safely | 766 | this.frame.dispose(); |
| 766 | this.frame.dispose(); | 767 | |
| 767 | System.exit(0); | 768 | } catch (IOException ex) { |
| 768 | } else { | 769 | throw new Error(ex); |
| 769 | // ask to save before closing | 770 | } |
| 770 | showDiscardDiag((response) -> { | 771 | } else if (response == JOptionPane.NO_OPTION) |
| 771 | if (response == JOptionPane.YES_OPTION) | 772 | this.frame.dispose(); |
| 772 | { | 773 | |
| 773 | try { | 774 | return null; |
| 774 | this.saveMapping(); | 775 | }, "Save and exit", "Discard changes", "Cancel"); |
| 775 | this.frame.dispose(); | 776 | } |
| 776 | 777 | } | |
| 777 | } catch (IOException ex) { | 778 | |
| 778 | throw new Error(ex); | 779 | public void redraw() { |
| 779 | } | 780 | this.frame.validate(); |
| 780 | } | 781 | this.frame.repaint(); |
| 781 | else if (response == JOptionPane.NO_OPTION) | 782 | } |
| 782 | this.frame.dispose(); | 783 | |
| 783 | 784 | public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException { | |
| 784 | return null; | 785 | // package rename |
| 785 | }, "Save and exit", "Discard changes", "Cancel"); | 786 | if (data instanceof String) { |
| 786 | } | 787 | for (int i = 0; i < node.getChildCount(); i++) { |
| 787 | } | 788 | data = Descriptor.toJvmName((String) data); |
| 788 | 789 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); | |
| 789 | public void redraw() { | 790 | ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); |
| 790 | this.frame.validate(); | 791 | ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); |
| 791 | this.frame.repaint(); | 792 | this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); |
| 792 | } | 793 | childNode.setUserObject(dataChild); |
| 793 | 794 | } | |
| 794 | public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException | 795 | node.setUserObject(data); |
| 795 | { | 796 | // Ob package will never be modified, just reload deob view |
| 796 | // package rename | 797 | this.deobfPanel.deobfClasses.reload(); |
| 797 | if (data instanceof String) | 798 | } |
| 798 | { | 799 | // class rename |
| 799 | for (int i = 0; i < node.getChildCount(); i++) | 800 | else if (data instanceof ClassEntry) |
| 800 | { | 801 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); |
| 801 | data = Descriptor.toJvmName((String) data); | 802 | } |
| 802 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); | 803 | |
| 803 | ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); | 804 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) { |
| 804 | ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); | 805 | String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); |
| 805 | this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); | 806 | String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); |
| 806 | childNode.setUserObject(dataChild); | 807 | moveClassTree(deobfReference, newName, oldEntry == null, |
| 807 | } | 808 | newEntry == null); |
| 808 | node.setUserObject(data); | 809 | } |
| 809 | // Ob package will never be modified, just reload deob view | 810 | |
| 810 | this.deobfPanel.deobfClasses.reload(); | 811 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { |
| 811 | } | 812 | ClassEntry oldEntry = deobfReference.entry.getClassEntry(); |
| 812 | // class rename | 813 | ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); |
| 813 | else if (data instanceof ClassEntry) | 814 | |
| 814 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); | 815 | // Ob -> deob |
| 815 | } | 816 | if (isOldOb && !isNewOb) { |
| 816 | 817 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | |
| 817 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) | 818 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); |
| 818 | { | 819 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); |
| 819 | String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); | 820 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); |
| 820 | String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); | 821 | this.deobfPanel.deobfClasses.reload(); |
| 821 | moveClassTree(deobfReference, newName, oldEntry == null, | 822 | this.obfPanel.obfClasses.reload(); |
| 822 | newEntry == null); | 823 | } |
| 823 | } | 824 | // Deob -> ob |
| 824 | 825 | else if (isNewOb && !isOldOb) { | |
| 825 | public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) | 826 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); |
| 826 | { | 827 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); |
| 827 | ClassEntry oldEntry = deobfReference.entry.getClassEntry(); | 828 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); |
| 828 | ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); | 829 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); |
| 829 | 830 | this.deobfPanel.deobfClasses.reload(); | |
| 830 | // Ob -> deob | 831 | this.obfPanel.obfClasses.reload(); |
| 831 | if (isOldOb && !isNewOb) | 832 | } |
| 832 | { | 833 | // Local move |
| 833 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | 834 | else if (isOldOb) { |
| 834 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); | 835 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); |
| 835 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); | 836 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); |
| 836 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); | 837 | this.obfPanel.obfClasses.reload(); |
| 837 | this.deobfPanel.deobfClasses.reload(); | 838 | } else { |
| 838 | this.obfPanel.obfClasses.reload(); | 839 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); |
| 839 | } | 840 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); |
| 840 | // Deob -> ob | 841 | this.deobfPanel.deobfClasses.reload(); |
| 841 | else if (isNewOb && !isOldOb) | 842 | } |
| 842 | { | 843 | } |
| 843 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); | ||
| 844 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); | ||
| 845 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); | ||
| 846 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); | ||
| 847 | this.deobfPanel.deobfClasses.reload(); | ||
| 848 | this.obfPanel.obfClasses.reload(); | ||
| 849 | } | ||
| 850 | // Local move | ||
| 851 | else if (isOldOb) | ||
| 852 | { | ||
| 853 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); | ||
| 854 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); | ||
| 855 | this.obfPanel.obfClasses.reload(); | ||
| 856 | } | ||
| 857 | else | ||
| 858 | { | ||
| 859 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); | ||
| 860 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); | ||
| 861 | this.deobfPanel.deobfClasses.reload(); | ||
| 862 | } | ||
| 863 | } | ||
| 864 | } | 844 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index 68fd4843..1b461da7 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -30,326 +31,321 @@ import java.util.jar.JarFile; | |||
| 30 | 31 | ||
| 31 | public class GuiController { | 32 | public class GuiController { |
| 32 | 33 | ||
| 33 | private Deobfuscator deobfuscator; | 34 | private Deobfuscator deobfuscator; |
| 34 | private Gui gui; | 35 | private Gui gui; |
| 35 | private SourceIndex index; | 36 | private SourceIndex index; |
| 36 | private ClassEntry currentObfClass; | 37 | private ClassEntry currentObfClass; |
| 37 | private boolean isDirty; | 38 | private boolean isDirty; |
| 38 | private Deque<EntryReference<Entry, Entry>> referenceStack; | 39 | private Deque<EntryReference<Entry, Entry>> referenceStack; |
| 39 | 40 | ||
| 40 | public GuiController(Gui gui) { | 41 | public GuiController(Gui gui) { |
| 41 | this.gui = gui; | 42 | this.gui = gui; |
| 42 | this.deobfuscator = null; | 43 | this.deobfuscator = null; |
| 43 | this.index = null; | 44 | this.index = null; |
| 44 | this.currentObfClass = null; | 45 | this.currentObfClass = null; |
| 45 | this.isDirty = false; | 46 | this.isDirty = false; |
| 46 | this.referenceStack = Queues.newArrayDeque(); | 47 | this.referenceStack = Queues.newArrayDeque(); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | public boolean isDirty() { | 50 | public boolean isDirty() { |
| 50 | return this.isDirty; | 51 | return this.isDirty; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | public void openJar(final JarFile jar) { | 54 | public void openJar(final JarFile jar) { |
| 54 | this.gui.onStartOpenJar(); | 55 | this.gui.onStartOpenJar(); |
| 55 | this.deobfuscator = new Deobfuscator(jar); | 56 | this.deobfuscator = new Deobfuscator(jar); |
| 56 | this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); | 57 | this.gui.onFinishOpenJar(this.deobfuscator.getJarName()); |
| 57 | refreshClasses(); | 58 | refreshClasses(); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | public void closeJar() { | 61 | public void closeJar() { |
| 61 | this.deobfuscator = null; | 62 | this.deobfuscator = null; |
| 62 | this.gui.onCloseJar(); | 63 | this.gui.onCloseJar(); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | public void openEnigmaMappings(File file) throws IOException, MappingParseException { | 66 | public void openEnigmaMappings(File file) throws IOException, MappingParseException { |
| 66 | this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); | 67 | this.deobfuscator.setMappings(new MappingsEnigmaReader().read(file)); |
| 67 | this.isDirty = false; | 68 | this.isDirty = false; |
| 68 | this.gui.setMappingsFile(file); | 69 | this.gui.setMappingsFile(file); |
| 69 | refreshClasses(); | 70 | refreshClasses(); |
| 70 | refreshCurrentClass(); | 71 | refreshCurrentClass(); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | public void saveMappings(File file) throws IOException { | 74 | public void saveMappings(File file) throws IOException { |
| 74 | Mappings mappings = this.deobfuscator.getMappings(); | 75 | Mappings mappings = this.deobfuscator.getMappings(); |
| 75 | switch (mappings.getOriginMappingFormat()) | 76 | switch (mappings.getOriginMappingFormat()) { |
| 76 | { | 77 | case SRG_FILE: |
| 77 | case SRG_FILE: | 78 | saveSRGMappings(file); |
| 78 | saveSRGMappings(file); | 79 | break; |
| 79 | break; | 80 | default: |
| 80 | default: | 81 | saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat()); |
| 81 | saveEnigmaMappings(file, Mappings.FormatType.ENIGMA_FILE != mappings.getOriginMappingFormat()); | 82 | break; |
| 82 | break; | 83 | } |
| 83 | } | 84 | |
| 84 | 85 | } | |
| 85 | } | 86 | |
| 86 | 87 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { | |
| 87 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { | 88 | this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); |
| 88 | this.deobfuscator.getMappings().saveEnigmaMappings(file, isDirectoryFormat); | 89 | this.isDirty = false; |
| 89 | this.isDirty = false; | 90 | } |
| 90 | } | 91 | |
| 91 | 92 | public void saveSRGMappings(File file) throws IOException { | |
| 92 | public void saveSRGMappings(File file) throws IOException { | 93 | this.deobfuscator.getMappings().saveSRGMappings(file); |
| 93 | this.deobfuscator.getMappings().saveSRGMappings(file); | 94 | this.isDirty = false; |
| 94 | this.isDirty = false; | 95 | } |
| 95 | } | 96 | |
| 96 | 97 | public void closeMappings() { | |
| 97 | public void closeMappings() { | 98 | this.deobfuscator.setMappings(null); |
| 98 | this.deobfuscator.setMappings(null); | 99 | this.gui.setMappingsFile(null); |
| 99 | this.gui.setMappingsFile(null); | 100 | refreshClasses(); |
| 100 | refreshClasses(); | 101 | refreshCurrentClass(); |
| 101 | refreshCurrentClass(); | 102 | } |
| 102 | } | 103 | |
| 103 | 104 | public void rebuildMethodNames() { | |
| 104 | public void rebuildMethodNames() { | 105 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress)); |
| 105 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.rebuildMethodNames(progress)); | 106 | } |
| 106 | } | 107 | |
| 107 | 108 | public void exportSource(final File dirOut) { | |
| 108 | public void exportSource(final File dirOut) { | 109 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); |
| 109 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); | 110 | } |
| 110 | } | 111 | |
| 111 | 112 | public void exportJar(final File fileOut) { | |
| 112 | public void exportJar(final File fileOut) { | 113 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); |
| 113 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); | 114 | } |
| 114 | } | 115 | |
| 115 | 116 | public Token getToken(int pos) { | |
| 116 | public Token getToken(int pos) { | 117 | if (this.index == null) { |
| 117 | if (this.index == null) { | 118 | return null; |
| 118 | return null; | 119 | } |
| 119 | } | 120 | return this.index.getReferenceToken(pos); |
| 120 | return this.index.getReferenceToken(pos); | 121 | } |
| 121 | } | 122 | |
| 122 | 123 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | |
| 123 | public EntryReference<Entry, Entry> getDeobfReference(Token token) { | 124 | if (this.index == null) { |
| 124 | if (this.index == null) { | 125 | return null; |
| 125 | return null; | 126 | } |
| 126 | } | 127 | return this.index.getDeobfReference(token); |
| 127 | return this.index.getDeobfReference(token); | 128 | } |
| 128 | } | 129 | |
| 129 | 130 | public ReadableToken getReadableToken(Token token) { | |
| 130 | public ReadableToken getReadableToken(Token token) { | 131 | if (this.index == null) { |
| 131 | if (this.index == null) { | 132 | return null; |
| 132 | return null; | 133 | } |
| 133 | } | 134 | return new ReadableToken( |
| 134 | return new ReadableToken( | 135 | this.index.getLineNumber(token.start), |
| 135 | this.index.getLineNumber(token.start), | 136 | this.index.getColumnNumber(token.start), |
| 136 | this.index.getColumnNumber(token.start), | 137 | this.index.getColumnNumber(token.end) |
| 137 | this.index.getColumnNumber(token.end) | 138 | ); |
| 138 | ); | 139 | } |
| 139 | } | 140 | |
| 140 | 141 | public boolean entryHasDeobfuscatedName(Entry deobfEntry) { | |
| 141 | public boolean entryHasDeobfuscatedName(Entry deobfEntry) { | 142 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); |
| 142 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.obfuscateEntry(deobfEntry)); | 143 | } |
| 143 | } | 144 | |
| 144 | 145 | public boolean entryIsInJar(Entry deobfEntry) { | |
| 145 | public boolean entryIsInJar(Entry deobfEntry) { | 146 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); |
| 146 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.obfuscateEntry(deobfEntry)); | 147 | } |
| 147 | } | 148 | |
| 148 | 149 | public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { | |
| 149 | public boolean referenceIsRenameable(EntryReference<Entry, Entry> deobfReference) { | 150 | return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true); |
| 150 | return this.deobfuscator.isRenameable(this.deobfuscator.obfuscateReference(deobfReference), true); | 151 | } |
| 151 | } | 152 | |
| 152 | 153 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | |
| 153 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | 154 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); |
| 154 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); | 155 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); |
| 155 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getClassInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); | 156 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); |
| 156 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); | 157 | } |
| 157 | } | 158 | |
| 158 | 159 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | |
| 159 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | 160 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); |
| 160 | ClassEntry obfClassEntry = this.deobfuscator.obfuscateEntry(deobfClassEntry); | 161 | return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); |
| 161 | return this.deobfuscator.getJarIndex().getClassImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfClassEntry); | 162 | } |
| 162 | } | 163 | |
| 163 | 164 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | |
| 164 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | 165 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); |
| 165 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); | 166 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); |
| 166 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getJarIndex().getMethodInheritance(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); | 167 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); |
| 167 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); | 168 | } |
| 168 | } | 169 | |
| 169 | 170 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | |
| 170 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | 171 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); |
| 171 | MethodEntry obfMethodEntry = this.deobfuscator.obfuscateEntry(deobfMethodEntry); | 172 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); |
| 172 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getJarIndex().getMethodImplementations(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfMethodEntry); | 173 | if (rootNodes.isEmpty()) { |
| 173 | if (rootNodes.isEmpty()) { | 174 | return null; |
| 174 | return null; | 175 | } |
| 175 | } | 176 | if (rootNodes.size() > 1) { |
| 176 | if (rootNodes.size() > 1) { | 177 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); |
| 177 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); | 178 | } |
| 178 | } | 179 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); |
| 179 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); | 180 | } |
| 180 | } | 181 | |
| 181 | 182 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | |
| 182 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | 183 | FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); |
| 183 | FieldEntry obfFieldEntry = this.deobfuscator.obfuscateEntry(deobfFieldEntry); | 184 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); |
| 184 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfFieldEntry); | 185 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 185 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 186 | return rootNode; |
| 186 | return rootNode; | 187 | } |
| 187 | } | 188 | |
| 188 | 189 | public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { | |
| 189 | public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { | 190 | BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); |
| 190 | BehaviorEntry obfBehaviorEntry = this.deobfuscator.obfuscateEntry(deobfBehaviorEntry); | 191 | BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); |
| 191 | BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode(this.deobfuscator.getTranslator(TranslationDirection.Deobfuscating), obfBehaviorEntry); | 192 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 192 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 193 | return rootNode; |
| 193 | return rootNode; | 194 | } |
| 194 | } | 195 | |
| 195 | 196 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { | |
| 196 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { | 197 | rename(deobfReference, newName, true, true); |
| 197 | rename(deobfReference, newName, true, true); | 198 | } |
| 198 | } | 199 | |
| 199 | 200 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) { | |
| 200 | public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache) | 201 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); |
| 201 | { | 202 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache); |
| 202 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 203 | this.isDirty = true; |
| 203 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache); | 204 | |
| 204 | this.isDirty = true; | 205 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) |
| 205 | 206 | this.gui.moveClassTree(deobfReference, newName); | |
| 206 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 207 | refreshCurrentClass(obfReference); |
| 207 | this.gui.moveClassTree(deobfReference, newName); | 208 | |
| 208 | refreshCurrentClass(obfReference); | 209 | } |
| 209 | 210 | ||
| 210 | } | 211 | public void removeMapping(EntryReference<Entry, Entry> deobfReference) { |
| 211 | 212 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 212 | public void removeMapping(EntryReference<Entry, Entry> deobfReference) { | 213 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); |
| 213 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 214 | this.isDirty = true; |
| 214 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); | 215 | if (deobfReference.entry instanceof ClassEntry) |
| 215 | this.isDirty = true; | 216 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); |
| 216 | if (deobfReference.entry instanceof ClassEntry) | 217 | refreshCurrentClass(obfReference); |
| 217 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); | 218 | } |
| 218 | refreshCurrentClass(obfReference); | 219 | |
| 219 | } | 220 | public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { |
| 220 | 221 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 221 | public void markAsDeobfuscated(EntryReference<Entry, Entry> deobfReference) { | 222 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); |
| 222 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 223 | this.isDirty = true; |
| 223 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); | 224 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) |
| 224 | this.isDirty = true; | 225 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); |
| 225 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 226 | refreshCurrentClass(obfReference); |
| 226 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); | 227 | } |
| 227 | refreshCurrentClass(obfReference); | 228 | |
| 228 | } | 229 | public void openDeclaration(Entry deobfEntry) { |
| 229 | 230 | if (deobfEntry == null) { | |
| 230 | public void openDeclaration(Entry deobfEntry) { | 231 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 231 | if (deobfEntry == null) { | 232 | } |
| 232 | throw new IllegalArgumentException("Entry cannot be null!"); | 233 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); |
| 233 | } | 234 | } |
| 234 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); | 235 | |
| 235 | } | 236 | public void openReference(EntryReference<Entry, Entry> deobfReference) { |
| 236 | 237 | if (deobfReference == null) { | |
| 237 | public void openReference(EntryReference<Entry, Entry> deobfReference) { | 238 | throw new IllegalArgumentException("Reference cannot be null!"); |
| 238 | if (deobfReference == null) { | 239 | } |
| 239 | throw new IllegalArgumentException("Reference cannot be null!"); | 240 | |
| 240 | } | 241 | // get the reference target class |
| 241 | 242 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | |
| 242 | // get the reference target class | 243 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); |
| 243 | EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); | 244 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { |
| 244 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); | 245 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); |
| 245 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { | 246 | } |
| 246 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); | 247 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { |
| 247 | } | 248 | // deobfuscate the class, then navigate to the reference |
| 248 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { | 249 | this.currentObfClass = obfClassEntry; |
| 249 | // deobfuscate the class, then navigate to the reference | 250 | deobfuscate(this.currentObfClass, obfReference); |
| 250 | this.currentObfClass = obfClassEntry; | 251 | } else { |
| 251 | deobfuscate(this.currentObfClass, obfReference); | 252 | showReference(obfReference); |
| 252 | } else { | 253 | } |
| 253 | showReference(obfReference); | 254 | } |
| 254 | } | 255 | |
| 255 | } | 256 | private void showReference(EntryReference<Entry, Entry> obfReference) { |
| 256 | 257 | EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); | |
| 257 | private void showReference(EntryReference<Entry, Entry> obfReference) { | 258 | Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); |
| 258 | EntryReference<Entry, Entry> deobfReference = this.deobfuscator.deobfuscateReference(obfReference); | 259 | if (tokens.isEmpty()) { |
| 259 | Collection<Token> tokens = this.index.getReferenceTokens(deobfReference); | 260 | // DEBUG |
| 260 | if (tokens.isEmpty()) { | 261 | System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); |
| 261 | // DEBUG | 262 | } else { |
| 262 | System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, this.currentObfClass)); | 263 | this.gui.showTokens(tokens); |
| 263 | } else { | 264 | } |
| 264 | this.gui.showTokens(tokens); | 265 | } |
| 265 | } | 266 | |
| 266 | } | 267 | public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { |
| 267 | 268 | this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); | |
| 268 | public void savePreviousReference(EntryReference<Entry, Entry> deobfReference) { | 269 | } |
| 269 | this.referenceStack.push(this.deobfuscator.obfuscateReference(deobfReference)); | 270 | |
| 270 | } | 271 | public void openPreviousReference() { |
| 271 | 272 | if (hasPreviousLocation()) { | |
| 272 | public void openPreviousReference() { | 273 | openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); |
| 273 | if (hasPreviousLocation()) { | 274 | } |
| 274 | openReference(this.deobfuscator.deobfuscateReference(this.referenceStack.pop())); | 275 | } |
| 275 | } | 276 | |
| 276 | } | 277 | public boolean hasPreviousLocation() { |
| 277 | 278 | return !this.referenceStack.isEmpty(); | |
| 278 | public boolean hasPreviousLocation() { | 279 | } |
| 279 | return !this.referenceStack.isEmpty(); | 280 | |
| 280 | } | 281 | private void refreshClasses() { |
| 281 | 282 | List<ClassEntry> obfClasses = Lists.newArrayList(); | |
| 282 | private void refreshClasses() { | 283 | List<ClassEntry> deobfClasses = Lists.newArrayList(); |
| 283 | List<ClassEntry> obfClasses = Lists.newArrayList(); | 284 | this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); |
| 284 | List<ClassEntry> deobfClasses = Lists.newArrayList(); | 285 | this.gui.setObfClasses(obfClasses); |
| 285 | this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); | 286 | this.gui.setDeobfClasses(deobfClasses); |
| 286 | this.gui.setObfClasses(obfClasses); | 287 | } |
| 287 | this.gui.setDeobfClasses(deobfClasses); | 288 | |
| 288 | } | 289 | public void refreshCurrentClass() { |
| 289 | 290 | refreshCurrentClass(null); | |
| 290 | public void refreshCurrentClass() { | 291 | } |
| 291 | refreshCurrentClass(null); | 292 | |
| 292 | } | 293 | private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { |
| 293 | 294 | if (this.currentObfClass != null) { | |
| 294 | private void refreshCurrentClass(EntryReference<Entry, Entry> obfReference) { | 295 | deobfuscate(this.currentObfClass, obfReference); |
| 295 | if (this.currentObfClass != null) { | 296 | } |
| 296 | deobfuscate(this.currentObfClass, obfReference); | 297 | } |
| 297 | } | 298 | |
| 298 | } | 299 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { |
| 299 | 300 | ||
| 300 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry, Entry> obfReference) { | 301 | this.gui.setSource("(deobfuscating...)"); |
| 301 | 302 | ||
| 302 | this.gui.setSource("(deobfuscating...)"); | 303 | // run the deobfuscator in a separate thread so we don't block the GUI event queue |
| 303 | 304 | new Thread(() -> | |
| 304 | // run the deobfuscator in a separate thread so we don't block the GUI event queue | 305 | { |
| 305 | new Thread(() -> | 306 | // decompile,deobfuscate the bytecode |
| 306 | { | 307 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); |
| 307 | // decompile,deobfuscate the bytecode | 308 | if (sourceTree == null) { |
| 308 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getClassName()); | 309 | // decompilation of this class is not supported |
| 309 | if (sourceTree == null) { | 310 | gui.setSource("Unable to find class: " + classEntry); |
| 310 | // decompilation of this class is not supported | 311 | return; |
| 311 | gui.setSource("Unable to find class: " + classEntry); | 312 | } |
| 312 | return; | 313 | String source = deobfuscator.getSource(sourceTree); |
| 313 | } | 314 | index = deobfuscator.getSourceIndex(sourceTree, source); |
| 314 | String source = deobfuscator.getSource(sourceTree); | 315 | gui.setSource(index.getSource()); |
| 315 | index = deobfuscator.getSourceIndex(sourceTree, source); | 316 | if (obfReference != null) { |
| 316 | gui.setSource(index.getSource()); | 317 | showReference(obfReference); |
| 317 | if (obfReference != null) { | 318 | } |
| 318 | showReference(obfReference); | 319 | |
| 319 | } | 320 | // set the highlighted tokens |
| 320 | 321 | List<Token> obfuscatedTokens = Lists.newArrayList(); | |
| 321 | // set the highlighted tokens | 322 | List<Token> deobfuscatedTokens = Lists.newArrayList(); |
| 322 | List<Token> obfuscatedTokens = Lists.newArrayList(); | 323 | List<Token> otherTokens = Lists.newArrayList(); |
| 323 | List<Token> deobfuscatedTokens = Lists.newArrayList(); | 324 | for (Token token : index.referenceTokens()) { |
| 324 | List<Token> otherTokens = Lists.newArrayList(); | 325 | EntryReference<Entry, Entry> reference = index.getDeobfReference(token); |
| 325 | for (Token token : index.referenceTokens()) { | 326 | if (referenceIsRenameable(reference)) { |
| 326 | EntryReference<Entry, Entry> reference = index.getDeobfReference(token); | 327 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { |
| 327 | if (referenceIsRenameable(reference)) { | 328 | deobfuscatedTokens.add(token); |
| 328 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { | 329 | } else { |
| 329 | deobfuscatedTokens.add(token); | 330 | obfuscatedTokens.add(token); |
| 330 | } else { | 331 | } |
| 331 | obfuscatedTokens.add(token); | 332 | } else { |
| 332 | } | 333 | otherTokens.add(token); |
| 333 | } else { | 334 | } |
| 334 | otherTokens.add(token); | 335 | } |
| 335 | } | 336 | gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); |
| 336 | } | 337 | }).start(); |
| 337 | gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); | 338 | } |
| 338 | }).start(); | 339 | |
| 339 | } | 340 | public Deobfuscator getDeobfuscator() { |
| 340 | 341 | return deobfuscator; | |
| 341 | public Deobfuscator getDeobfuscator() | 342 | } |
| 342 | { | 343 | |
| 343 | return deobfuscator; | 344 | public void modifierChange(ItemEvent event) { |
| 344 | } | 345 | if (event.getStateChange() == ItemEvent.SELECTED) { |
| 345 | 346 | deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem()); | |
| 346 | public void modifierChange(ItemEvent event) | 347 | this.isDirty = true; |
| 347 | { | 348 | refreshCurrentClass(); |
| 348 | if (event.getStateChange() == ItemEvent.SELECTED) | 349 | } |
| 349 | { | 350 | } |
| 350 | deobfuscator.changeModifier(gui.reference.entry, (Mappings.EntryModifier) event.getItem()); | ||
| 351 | this.isDirty = true; | ||
| 352 | refreshCurrentClass(); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | 351 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java index 85b65b0a..8bf57d38 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java +++ b/src/main/java/cuchaz/enigma/gui/GuiTricks.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import javax.swing.*; | 14 | import javax.swing.*; |
| @@ -17,26 +18,26 @@ import java.util.Arrays; | |||
| 17 | 18 | ||
| 18 | public class GuiTricks { | 19 | public class GuiTricks { |
| 19 | 20 | ||
| 20 | public static JLabel unboldLabel(JLabel label) { | 21 | public static JLabel unboldLabel(JLabel label) { |
| 21 | Font font = label.getFont(); | 22 | Font font = label.getFont(); |
| 22 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); | 23 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); |
| 23 | return label; | 24 | return label; |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public static void deactivateButton(JButton button) { | 27 | public static void deactivateButton(JButton button) { |
| 27 | button.setEnabled(false); | 28 | button.setEnabled(false); |
| 28 | button.setText(""); | 29 | button.setText(""); |
| 29 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { | 30 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { |
| 30 | button.removeActionListener(listener); | 31 | button.removeActionListener(listener); |
| 31 | } | 32 | } |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | public static void activateButton(JButton button, String text, ActionListener newListener) { | 35 | public static void activateButton(JButton button, String text, ActionListener newListener) { |
| 35 | button.setText(text); | 36 | button.setText(text); |
| 36 | button.setEnabled(true); | 37 | button.setEnabled(true); |
| 37 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { | 38 | for (ActionListener listener : Arrays.asList(button.getActionListeners())) { |
| 38 | button.removeActionListener(listener); | 39 | button.removeActionListener(listener); |
| 39 | } | 40 | } |
| 40 | button.addActionListener(newListener); | 41 | button.addActionListener(newListener); |
| 41 | } | 42 | } |
| 42 | } | 43 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java index 671f85fe..4f5231f1 100644 --- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java +++ b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java | |||
| @@ -8,25 +8,11 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 15 | |||
| 16 | import java.awt.BorderLayout; | ||
| 17 | import java.awt.Container; | ||
| 18 | import java.awt.Dimension; | ||
| 19 | import java.awt.FlowLayout; | ||
| 20 | import java.awt.event.ActionListener; | ||
| 21 | import java.awt.event.KeyAdapter; | ||
| 22 | import java.awt.event.KeyEvent; | ||
| 23 | import java.util.Collection; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.Map; | ||
| 26 | |||
| 27 | import javax.swing.*; | ||
| 28 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 29 | |||
| 30 | import cuchaz.enigma.Constants; | 16 | import cuchaz.enigma.Constants; |
| 31 | import cuchaz.enigma.Deobfuscator; | 17 | import cuchaz.enigma.Deobfuscator; |
| 32 | import cuchaz.enigma.analysis.SourceIndex; | 18 | import cuchaz.enigma.analysis.SourceIndex; |
| @@ -39,403 +25,410 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 39 | import cuchaz.enigma.mapping.Entry; | 25 | import cuchaz.enigma.mapping.Entry; |
| 40 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 26 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 41 | 27 | ||
| 28 | import javax.swing.*; | ||
| 29 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 30 | import java.awt.*; | ||
| 31 | import java.awt.event.ActionListener; | ||
| 32 | import java.awt.event.KeyAdapter; | ||
| 33 | import java.awt.event.KeyEvent; | ||
| 34 | import java.util.Collection; | ||
| 35 | import java.util.List; | ||
| 36 | import java.util.Map; | ||
| 42 | 37 | ||
| 43 | public class MemberMatchingGui<T extends Entry> { | 38 | public class MemberMatchingGui<T extends Entry> { |
| 44 | 39 | ||
| 45 | private enum SourceType { | 40 | // controls |
| 46 | Matched { | 41 | private JFrame frame; |
| 47 | @Override | 42 | private Map<SourceType, JRadioButton> sourceTypeButtons; |
| 48 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { | 43 | private ClassSelector sourceClasses; |
| 49 | return matches.getSourceClassesWithoutUnmatchedEntries(); | 44 | private CodeReader sourceReader; |
| 50 | } | 45 | private CodeReader destReader; |
| 51 | }, | 46 | private JButton matchButton; |
| 52 | Unmatched { | 47 | private JButton unmatchableButton; |
| 53 | @Override | 48 | private JLabel sourceLabel; |
| 54 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { | 49 | private JLabel destLabel; |
| 55 | return matches.getSourceClassesWithUnmatchedEntries(); | 50 | private HighlightPainter unmatchedHighlightPainter; |
| 56 | } | 51 | private HighlightPainter matchedHighlightPainter; |
| 57 | }; | 52 | private ClassMatches classMatches; |
| 58 | 53 | private MemberMatches<T> memberMatches; | |
| 59 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | 54 | private Deobfuscator sourceDeobfuscator; |
| 60 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | 55 | private Deobfuscator destDeobfuscator; |
| 61 | button.setActionCommand(name()); | 56 | private SaveListener<T> saveListener; |
| 62 | button.addActionListener(listener); | 57 | private SourceType sourceType; |
| 63 | group.add(button); | 58 | private ClassEntry obfSourceClass; |
| 64 | return button; | 59 | private ClassEntry obfDestClass; |
| 65 | } | 60 | private T obfSourceEntry; |
| 66 | 61 | private T obfDestEntry; | |
| 67 | public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches); | 62 | public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 68 | 63 | ||
| 69 | public static SourceType getDefault() { | 64 | this.classMatches = classMatches; |
| 70 | return values()[0]; | 65 | memberMatches = fieldMatches; |
| 71 | } | 66 | this.sourceDeobfuscator = sourceDeobfuscator; |
| 72 | } | 67 | this.destDeobfuscator = destDeobfuscator; |
| 73 | 68 | ||
| 74 | public interface SaveListener<T extends Entry> { | 69 | // init frame |
| 75 | void save(MemberMatches<T> matches); | 70 | frame = new JFrame(Constants.NAME + " - Member Matcher"); |
| 76 | } | 71 | final Container pane = frame.getContentPane(); |
| 77 | 72 | pane.setLayout(new BorderLayout()); | |
| 78 | // controls | 73 | |
| 79 | private JFrame frame; | 74 | // init classes side |
| 80 | private Map<SourceType, JRadioButton> sourceTypeButtons; | 75 | JPanel classesPanel = new JPanel(); |
| 81 | private ClassSelector sourceClasses; | 76 | classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); |
| 82 | private CodeReader sourceReader; | 77 | classesPanel.setPreferredSize(new Dimension(200, 0)); |
| 83 | private CodeReader destReader; | 78 | pane.add(classesPanel, BorderLayout.WEST); |
| 84 | private JButton matchButton; | 79 | classesPanel.add(new JLabel("Classes")); |
| 85 | private JButton unmatchableButton; | 80 | |
| 86 | private JLabel sourceLabel; | 81 | // init source type radios |
| 87 | private JLabel destLabel; | 82 | JPanel sourceTypePanel = new JPanel(); |
| 88 | private HighlightPainter unmatchedHighlightPainter; | 83 | classesPanel.add(sourceTypePanel); |
| 89 | private HighlightPainter matchedHighlightPainter; | 84 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); |
| 90 | private ClassMatches classMatches; | 85 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); |
| 91 | private MemberMatches<T> memberMatches; | 86 | ButtonGroup sourceTypeButtons = new ButtonGroup(); |
| 92 | private Deobfuscator sourceDeobfuscator; | 87 | this.sourceTypeButtons = Maps.newHashMap(); |
| 93 | private Deobfuscator destDeobfuscator; | 88 | for (SourceType sourceType : SourceType.values()) { |
| 94 | private SaveListener<T> saveListener; | 89 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); |
| 95 | private SourceType sourceType; | 90 | this.sourceTypeButtons.put(sourceType, button); |
| 96 | private ClassEntry obfSourceClass; | 91 | sourceTypePanel.add(button); |
| 97 | private ClassEntry obfDestClass; | 92 | } |
| 98 | private T obfSourceEntry; | 93 | |
| 99 | private T obfDestEntry; | 94 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); |
| 100 | 95 | sourceClasses.setSelectionListener(this::setSourceClass); | |
| 101 | public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 96 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); |
| 102 | 97 | classesPanel.add(sourceScroller); | |
| 103 | this.classMatches = classMatches; | 98 | |
| 104 | memberMatches = fieldMatches; | 99 | // init readers |
| 105 | this.sourceDeobfuscator = sourceDeobfuscator; | 100 | DefaultSyntaxKit.initKit(); |
| 106 | this.destDeobfuscator = destDeobfuscator; | 101 | sourceReader = new CodeReader(); |
| 107 | 102 | sourceReader.setSelectionListener(reference -> | |
| 108 | // init frame | 103 | { |
| 109 | frame = new JFrame(Constants.NAME + " - Member Matcher"); | 104 | if (reference != null) { |
| 110 | final Container pane = frame.getContentPane(); | 105 | onSelectSource(reference.entry); |
| 111 | pane.setLayout(new BorderLayout()); | 106 | } else { |
| 112 | 107 | onSelectSource(null); | |
| 113 | // init classes side | 108 | } |
| 114 | JPanel classesPanel = new JPanel(); | 109 | }); |
| 115 | classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); | 110 | destReader = new CodeReader(); |
| 116 | classesPanel.setPreferredSize(new Dimension(200, 0)); | 111 | destReader.setSelectionListener(reference -> |
| 117 | pane.add(classesPanel, BorderLayout.WEST); | 112 | { |
| 118 | classesPanel.add(new JLabel("Classes")); | 113 | if (reference != null) { |
| 119 | 114 | onSelectDest(reference.entry); | |
| 120 | // init source type radios | 115 | } else { |
| 121 | JPanel sourceTypePanel = new JPanel(); | 116 | onSelectDest(null); |
| 122 | classesPanel.add(sourceTypePanel); | 117 | } |
| 123 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); | 118 | }); |
| 124 | ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand())); | 119 | |
| 125 | ButtonGroup sourceTypeButtons = new ButtonGroup(); | 120 | // add key bindings |
| 126 | this.sourceTypeButtons = Maps.newHashMap(); | 121 | KeyAdapter keyListener = new KeyAdapter() { |
| 127 | for (SourceType sourceType : SourceType.values()) { | 122 | @Override |
| 128 | JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons); | 123 | public void keyPressed(KeyEvent event) { |
| 129 | this.sourceTypeButtons.put(sourceType, button); | 124 | if (event.getKeyCode() == KeyEvent.VK_M) |
| 130 | sourceTypePanel.add(button); | 125 | matchButton.doClick(); |
| 131 | } | 126 | } |
| 132 | 127 | }; | |
| 133 | sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false); | 128 | sourceReader.addKeyListener(keyListener); |
| 134 | sourceClasses.setSelectionListener(this::setSourceClass); | 129 | destReader.addKeyListener(keyListener); |
| 135 | JScrollPane sourceScroller = new JScrollPane(sourceClasses); | 130 | |
| 136 | classesPanel.add(sourceScroller); | 131 | // init all the splits |
| 137 | 132 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( | |
| 138 | // init readers | 133 | destReader)); |
| 139 | DefaultSyntaxKit.initKit(); | 134 | splitRight.setResizeWeight(0.5); // resize 50:50 |
| 140 | sourceReader = new CodeReader(); | 135 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); |
| 141 | sourceReader.setSelectionListener(reference -> | 136 | splitLeft.setResizeWeight(0); // let the right side take all the slack |
| 142 | { | 137 | pane.add(splitLeft, BorderLayout.CENTER); |
| 143 | if (reference != null) { | 138 | splitLeft.resetToPreferredSizes(); |
| 144 | onSelectSource(reference.entry); | 139 | |
| 145 | } else { | 140 | // init bottom panel |
| 146 | onSelectSource(null); | 141 | JPanel bottomPanel = new JPanel(); |
| 147 | } | 142 | bottomPanel.setLayout(new FlowLayout()); |
| 148 | }); | 143 | pane.add(bottomPanel, BorderLayout.SOUTH); |
| 149 | destReader = new CodeReader(); | 144 | |
| 150 | destReader.setSelectionListener(reference -> | 145 | matchButton = new JButton(); |
| 151 | { | 146 | unmatchableButton = new JButton(); |
| 152 | if (reference != null) { | 147 | |
| 153 | onSelectDest(reference.entry); | 148 | sourceLabel = new JLabel(); |
| 154 | } else { | 149 | bottomPanel.add(sourceLabel); |
| 155 | onSelectDest(null); | 150 | bottomPanel.add(matchButton); |
| 156 | } | 151 | bottomPanel.add(unmatchableButton); |
| 157 | }); | 152 | destLabel = new JLabel(); |
| 158 | 153 | bottomPanel.add(destLabel); | |
| 159 | // add key bindings | 154 | |
| 160 | KeyAdapter keyListener = new KeyAdapter() { | 155 | // show the frame |
| 161 | @Override | 156 | pane.doLayout(); |
| 162 | public void keyPressed(KeyEvent event) { | 157 | frame.setSize(1024, 576); |
| 163 | if (event.getKeyCode() == KeyEvent.VK_M) | 158 | frame.setMinimumSize(new Dimension(640, 480)); |
| 164 | matchButton.doClick(); | 159 | frame.setVisible(true); |
| 165 | } | 160 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 166 | }; | 161 | |
| 167 | sourceReader.addKeyListener(keyListener); | 162 | unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); |
| 168 | destReader.addKeyListener(keyListener); | 163 | matchedHighlightPainter = new DeobfuscatedHighlightPainter(); |
| 169 | 164 | ||
| 170 | // init all the splits | 165 | // init state |
| 171 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(sourceReader), new JScrollPane( | 166 | saveListener = null; |
| 172 | destReader)); | 167 | obfSourceClass = null; |
| 173 | splitRight.setResizeWeight(0.5); // resize 50:50 | 168 | obfDestClass = null; |
| 174 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); | 169 | obfSourceEntry = null; |
| 175 | splitLeft.setResizeWeight(0); // let the right side take all the slack | 170 | obfDestEntry = null; |
| 176 | pane.add(splitLeft, BorderLayout.CENTER); | 171 | setSourceType(SourceType.getDefault()); |
| 177 | splitLeft.resetToPreferredSizes(); | 172 | updateButtons(); |
| 178 | 173 | } | |
| 179 | // init bottom panel | 174 | |
| 180 | JPanel bottomPanel = new JPanel(); | 175 | protected void setSourceType(SourceType val) { |
| 181 | bottomPanel.setLayout(new FlowLayout()); | 176 | sourceType = val; |
| 182 | pane.add(bottomPanel, BorderLayout.SOUTH); | 177 | updateSourceClasses(); |
| 183 | 178 | } | |
| 184 | matchButton = new JButton(); | 179 | |
| 185 | unmatchableButton = new JButton(); | 180 | public void setSaveListener(SaveListener<T> val) { |
| 186 | 181 | saveListener = val; | |
| 187 | sourceLabel = new JLabel(); | 182 | } |
| 188 | bottomPanel.add(sourceLabel); | 183 | |
| 189 | bottomPanel.add(matchButton); | 184 | private void updateSourceClasses() { |
| 190 | bottomPanel.add(unmatchableButton); | 185 | |
| 191 | destLabel = new JLabel(); | 186 | String selectedPackage = sourceClasses.getSelectedPackage(); |
| 192 | bottomPanel.add(destLabel); | 187 | |
| 193 | 188 | List<ClassEntry> deobfClassEntries = Lists.newArrayList(); | |
| 194 | // show the frame | 189 | for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { |
| 195 | pane.doLayout(); | 190 | deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); |
| 196 | frame.setSize(1024, 576); | 191 | } |
| 197 | frame.setMinimumSize(new Dimension(640, 480)); | 192 | sourceClasses.setClasses(deobfClassEntries); |
| 198 | frame.setVisible(true); | 193 | |
| 199 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 194 | if (selectedPackage != null) { |
| 200 | 195 | sourceClasses.expandPackage(selectedPackage); | |
| 201 | unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); | 196 | } |
| 202 | matchedHighlightPainter = new DeobfuscatedHighlightPainter(); | 197 | |
| 203 | 198 | for (SourceType sourceType : SourceType.values()) { | |
| 204 | // init state | 199 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", |
| 205 | saveListener = null; | 200 | sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() |
| 206 | obfSourceClass = null; | 201 | )); |
| 207 | obfDestClass = null; | 202 | } |
| 208 | obfSourceEntry = null; | 203 | } |
| 209 | obfDestEntry = null; | 204 | |
| 210 | setSourceType(SourceType.getDefault()); | 205 | protected void setSourceClass(ClassEntry sourceClass) { |
| 211 | updateButtons(); | 206 | |
| 212 | } | 207 | obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); |
| 213 | 208 | obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | |
| 214 | protected void setSourceType(SourceType val) { | 209 | if (obfDestClass == null) { |
| 215 | sourceType = val; | 210 | throw new Error("No matching dest class for source class: " + obfSourceClass); |
| 216 | updateSourceClasses(); | 211 | } |
| 217 | } | 212 | |
| 218 | 213 | sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); | |
| 219 | public void setSaveListener(SaveListener<T> val) { | 214 | destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); |
| 220 | saveListener = val; | 215 | } |
| 221 | } | 216 | |
| 222 | 217 | protected void updateSourceHighlights() { | |
| 223 | private void updateSourceClasses() { | 218 | highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); |
| 224 | 219 | } | |
| 225 | String selectedPackage = sourceClasses.getSelectedPackage(); | 220 | |
| 226 | 221 | protected void updateDestHighlights() { | |
| 227 | List<ClassEntry> deobfClassEntries = Lists.newArrayList(); | 222 | highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); |
| 228 | for (ClassEntry entry : sourceType.getObfSourceClasses(memberMatches)) { | 223 | } |
| 229 | deobfClassEntries.add(sourceDeobfuscator.deobfuscateEntry(entry)); | 224 | |
| 230 | } | 225 | private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) { |
| 231 | sourceClasses.setClasses(deobfClassEntries); | 226 | reader.clearHighlights(); |
| 232 | 227 | // matched fields | |
| 233 | if (selectedPackage != null) { | 228 | updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); |
| 234 | sourceClasses.expandPackage(selectedPackage); | 229 | // unmatched fields |
| 235 | } | 230 | updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); |
| 236 | 231 | } | |
| 237 | for (SourceType sourceType : SourceType.values()) { | 232 | |
| 238 | sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", | 233 | private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) { |
| 239 | sourceType.name(), sourceType.getObfSourceClasses(memberMatches).size() | 234 | SourceIndex index = reader.getSourceIndex(); |
| 240 | )); | 235 | for (T obfT : entries) { |
| 241 | } | 236 | T deobfT = deobfuscator.deobfuscateEntry(obfT); |
| 242 | } | 237 | Token token = index.getDeclarationToken(deobfT); |
| 243 | 238 | if (token != null) { | |
| 244 | protected void setSourceClass(ClassEntry sourceClass) { | 239 | reader.setHighlightedToken(token, painter); |
| 245 | 240 | } | |
| 246 | obfSourceClass = sourceDeobfuscator.obfuscateEntry(sourceClass); | 241 | } |
| 247 | obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 242 | } |
| 248 | if (obfDestClass == null) { | 243 | |
| 249 | throw new Error("No matching dest class for source class: " + obfSourceClass); | 244 | private boolean isSelectionMatched() { |
| 250 | } | 245 | return obfSourceEntry != null && obfDestEntry != null |
| 251 | 246 | && memberMatches.isMatched(obfSourceEntry, obfDestEntry); | |
| 252 | sourceReader.decompileClass(obfSourceClass, sourceDeobfuscator, false, this::updateSourceHighlights); | 247 | } |
| 253 | destReader.decompileClass(obfDestClass, destDeobfuscator, false, this::updateDestHighlights); | 248 | |
| 254 | } | 249 | protected void onSelectSource(Entry source) { |
| 255 | 250 | ||
| 256 | protected void updateSourceHighlights() { | 251 | // start with no selection |
| 257 | highlightEntries(sourceReader, sourceDeobfuscator, memberMatches.matches().keySet(), memberMatches.getUnmatchedSourceEntries()); | 252 | if (isSelectionMatched()) { |
| 258 | } | 253 | setDest(null); |
| 259 | 254 | } | |
| 260 | protected void updateDestHighlights() { | 255 | setSource(null); |
| 261 | highlightEntries(destReader, destDeobfuscator, memberMatches.matches().values(), memberMatches.getUnmatchedDestEntries()); | 256 | |
| 262 | } | 257 | // then look for a valid source selection |
| 263 | 258 | if (source != null) { | |
| 264 | private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) { | 259 | |
| 265 | reader.clearHighlights(); | 260 | // this looks really scary, but it's actually ok |
| 266 | // matched fields | 261 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry |
| 267 | updateHighlighted(obfMatchedEntries, deobfuscator, reader, matchedHighlightPainter); | 262 | // and MemberMatches.hasSource() will only pass entries that actually match T |
| 268 | // unmatched fields | 263 | @SuppressWarnings("unchecked") |
| 269 | updateHighlighted(obfUnmatchedEntries, deobfuscator, reader, unmatchedHighlightPainter); | 264 | T sourceEntry = (T) source; |
| 270 | } | 265 | |
| 271 | 266 | T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); | |
| 272 | private void updateHighlighted(Collection<T> entries, Deobfuscator deobfuscator, CodeReader reader, HighlightPainter painter) | 267 | if (memberMatches.hasSource(obfSourceEntry)) { |
| 273 | { | 268 | setSource(obfSourceEntry); |
| 274 | SourceIndex index = reader.getSourceIndex(); | 269 | |
| 275 | for (T obfT : entries) { | 270 | // look for a matched dest too |
| 276 | T deobfT = deobfuscator.deobfuscateEntry(obfT); | 271 | T obfDestEntry = memberMatches.matches().get(obfSourceEntry); |
| 277 | Token token = index.getDeclarationToken(deobfT); | 272 | if (obfDestEntry != null) { |
| 278 | if (token != null) { | 273 | setDest(obfDestEntry); |
| 279 | reader.setHighlightedToken(token, painter); | 274 | } |
| 280 | } | 275 | } |
| 281 | } | 276 | } |
| 282 | } | 277 | |
| 283 | 278 | updateButtons(); | |
| 284 | private boolean isSelectionMatched() { | 279 | } |
| 285 | return obfSourceEntry != null && obfDestEntry != null | 280 | |
| 286 | && memberMatches.isMatched(obfSourceEntry, obfDestEntry); | 281 | protected void onSelectDest(Entry dest) { |
| 287 | } | 282 | |
| 288 | 283 | // start with no selection | |
| 289 | protected void onSelectSource(Entry source) { | 284 | if (isSelectionMatched()) { |
| 290 | 285 | setSource(null); | |
| 291 | // start with no selection | 286 | } |
| 292 | if (isSelectionMatched()) { | 287 | setDest(null); |
| 293 | setDest(null); | 288 | |
| 294 | } | 289 | // then look for a valid dest selection |
| 295 | setSource(null); | 290 | if (dest != null) { |
| 296 | 291 | ||
| 297 | // then look for a valid source selection | 292 | // this looks really scary, but it's actually ok |
| 298 | if (source != null) { | 293 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry |
| 299 | 294 | // and MemberMatches.hasSource() will only pass entries that actually match T | |
| 300 | // this looks really scary, but it's actually ok | 295 | @SuppressWarnings("unchecked") |
| 301 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry | 296 | T destEntry = (T) dest; |
| 302 | // and MemberMatches.hasSource() will only pass entries that actually match T | 297 | |
| 303 | @SuppressWarnings("unchecked") | 298 | T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); |
| 304 | T sourceEntry = (T) source; | 299 | if (memberMatches.hasDest(obfDestEntry)) { |
| 305 | 300 | setDest(obfDestEntry); | |
| 306 | T obfSourceEntry = sourceDeobfuscator.obfuscateEntry(sourceEntry); | 301 | |
| 307 | if (memberMatches.hasSource(obfSourceEntry)) { | 302 | // look for a matched source too |
| 308 | setSource(obfSourceEntry); | 303 | T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); |
| 309 | 304 | if (obfSourceEntry != null) { | |
| 310 | // look for a matched dest too | 305 | setSource(obfSourceEntry); |
| 311 | T obfDestEntry = memberMatches.matches().get(obfSourceEntry); | 306 | } |
| 312 | if (obfDestEntry != null) { | 307 | } |
| 313 | setDest(obfDestEntry); | 308 | } |
| 314 | } | 309 | |
| 315 | } | 310 | updateButtons(); |
| 316 | } | 311 | } |
| 317 | 312 | ||
| 318 | updateButtons(); | 313 | private void setSource(T obfEntry) { |
| 319 | } | 314 | if (obfEntry == null) { |
| 320 | 315 | obfSourceEntry = null; | |
| 321 | protected void onSelectDest(Entry dest) { | 316 | sourceLabel.setText(""); |
| 322 | 317 | } else { | |
| 323 | // start with no selection | 318 | obfSourceEntry = obfEntry; |
| 324 | if (isSelectionMatched()) { | 319 | sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); |
| 325 | setSource(null); | 320 | } |
| 326 | } | 321 | } |
| 327 | setDest(null); | 322 | |
| 328 | 323 | private void setDest(T obfEntry) { | |
| 329 | // then look for a valid dest selection | 324 | if (obfEntry == null) { |
| 330 | if (dest != null) { | 325 | obfDestEntry = null; |
| 331 | 326 | destLabel.setText(""); | |
| 332 | // this looks really scary, but it's actually ok | 327 | } else { |
| 333 | // Deobfuscator.obfuscateEntry can handle all implementations of Entry | 328 | obfDestEntry = obfEntry; |
| 334 | // and MemberMatches.hasSource() will only pass entries that actually match T | 329 | destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); |
| 335 | @SuppressWarnings("unchecked") | 330 | } |
| 336 | T destEntry = (T) dest; | 331 | } |
| 337 | 332 | ||
| 338 | T obfDestEntry = destDeobfuscator.obfuscateEntry(destEntry); | 333 | private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { |
| 339 | if (memberMatches.hasDest(obfDestEntry)) { | 334 | // show obfuscated and deobfuscated names, but no types/signatures |
| 340 | setDest(obfDestEntry); | 335 | T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); |
| 341 | 336 | return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); | |
| 342 | // look for a matched source too | 337 | } |
| 343 | T obfSourceEntry = memberMatches.matches().inverse().get(obfDestEntry); | 338 | |
| 344 | if (obfSourceEntry != null) { | 339 | private void updateButtons() { |
| 345 | setSource(obfSourceEntry); | 340 | |
| 346 | } | 341 | GuiTricks.deactivateButton(matchButton); |
| 347 | } | 342 | GuiTricks.deactivateButton(unmatchableButton); |
| 348 | } | 343 | |
| 349 | 344 | if (obfSourceEntry != null && obfDestEntry != null) { | |
| 350 | updateButtons(); | 345 | if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) |
| 351 | } | 346 | GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); |
| 352 | 347 | else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( | |
| 353 | private void setSource(T obfEntry) { | 348 | obfDestEntry)) |
| 354 | if (obfEntry == null) { | 349 | GuiTricks.activateButton(matchButton, "Match", event -> match()); |
| 355 | obfSourceEntry = null; | 350 | } else if (obfSourceEntry != null) |
| 356 | sourceLabel.setText(""); | 351 | GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); |
| 357 | } else { | 352 | } |
| 358 | obfSourceEntry = obfEntry; | 353 | |
| 359 | sourceLabel.setText(getEntryLabel(obfEntry, sourceDeobfuscator)); | 354 | protected void match() { |
| 360 | } | 355 | |
| 361 | } | 356 | // update the field matches |
| 362 | 357 | memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | |
| 363 | private void setDest(T obfEntry) { | 358 | save(); |
| 364 | if (obfEntry == null) { | 359 | |
| 365 | obfDestEntry = null; | 360 | // update the ui |
| 366 | destLabel.setText(""); | 361 | onSelectSource(null); |
| 367 | } else { | 362 | onSelectDest(null); |
| 368 | obfDestEntry = obfEntry; | 363 | updateSourceHighlights(); |
| 369 | destLabel.setText(getEntryLabel(obfEntry, destDeobfuscator)); | 364 | updateDestHighlights(); |
| 370 | } | 365 | updateSourceClasses(); |
| 371 | } | 366 | } |
| 372 | 367 | ||
| 373 | private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) { | 368 | protected void unmatch() { |
| 374 | // show obfuscated and deobfuscated names, but no types/signatures | 369 | |
| 375 | T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry); | 370 | // update the field matches |
| 376 | return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName()); | 371 | memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); |
| 377 | } | 372 | save(); |
| 378 | 373 | ||
| 379 | private void updateButtons() { | 374 | // update the ui |
| 380 | 375 | onSelectSource(null); | |
| 381 | GuiTricks.deactivateButton(matchButton); | 376 | onSelectDest(null); |
| 382 | GuiTricks.deactivateButton(unmatchableButton); | 377 | updateSourceHighlights(); |
| 383 | 378 | updateDestHighlights(); | |
| 384 | if (obfSourceEntry != null && obfDestEntry != null) { | 379 | updateSourceClasses(); |
| 385 | if (memberMatches.isMatched(obfSourceEntry, obfDestEntry)) | 380 | } |
| 386 | GuiTricks.activateButton(matchButton, "Unmatch", event -> unmatch()); | 381 | |
| 387 | else if (!memberMatches.isMatchedSourceEntry(obfSourceEntry) && !memberMatches.isMatchedDestEntry( | 382 | protected void unmatchable() { |
| 388 | obfDestEntry)) | 383 | |
| 389 | GuiTricks.activateButton(matchButton, "Match", event -> match()); | 384 | // update the field matches |
| 390 | } else if (obfSourceEntry != null) | 385 | memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); |
| 391 | GuiTricks.activateButton(unmatchableButton, "Set Unmatchable", event -> unmatchable()); | 386 | save(); |
| 392 | } | 387 | |
| 393 | 388 | // update the ui | |
| 394 | protected void match() { | 389 | onSelectSource(null); |
| 395 | 390 | onSelectDest(null); | |
| 396 | // update the field matches | 391 | updateSourceHighlights(); |
| 397 | memberMatches.makeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | 392 | updateDestHighlights(); |
| 398 | save(); | 393 | updateSourceClasses(); |
| 399 | 394 | } | |
| 400 | // update the ui | 395 | |
| 401 | onSelectSource(null); | 396 | private void save() { |
| 402 | onSelectDest(null); | 397 | if (saveListener != null) { |
| 403 | updateSourceHighlights(); | 398 | saveListener.save(memberMatches); |
| 404 | updateDestHighlights(); | 399 | } |
| 405 | updateSourceClasses(); | 400 | } |
| 406 | } | 401 | |
| 407 | 402 | private enum SourceType { | |
| 408 | protected void unmatch() { | 403 | Matched { |
| 409 | 404 | @Override | |
| 410 | // update the field matches | 405 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { |
| 411 | memberMatches.unmakeMatch(obfSourceEntry, obfDestEntry, sourceDeobfuscator, destDeobfuscator); | 406 | return matches.getSourceClassesWithoutUnmatchedEntries(); |
| 412 | save(); | 407 | } |
| 413 | 408 | }, | |
| 414 | // update the ui | 409 | Unmatched { |
| 415 | onSelectSource(null); | 410 | @Override |
| 416 | onSelectDest(null); | 411 | public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) { |
| 417 | updateSourceHighlights(); | 412 | return matches.getSourceClassesWithUnmatchedEntries(); |
| 418 | updateDestHighlights(); | 413 | } |
| 419 | updateSourceClasses(); | 414 | }; |
| 420 | } | 415 | |
| 421 | 416 | public static SourceType getDefault() { | |
| 422 | protected void unmatchable() { | 417 | return values()[0]; |
| 423 | 418 | } | |
| 424 | // update the field matches | 419 | |
| 425 | memberMatches.makeSourceUnmatchable(obfSourceEntry, sourceDeobfuscator); | 420 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { |
| 426 | save(); | 421 | JRadioButton button = new JRadioButton(name(), this == getDefault()); |
| 427 | 422 | button.setActionCommand(name()); | |
| 428 | // update the ui | 423 | button.addActionListener(listener); |
| 429 | onSelectSource(null); | 424 | group.add(button); |
| 430 | onSelectDest(null); | 425 | return button; |
| 431 | updateSourceHighlights(); | 426 | } |
| 432 | updateDestHighlights(); | 427 | |
| 433 | updateSourceClasses(); | 428 | public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches); |
| 434 | } | 429 | } |
| 435 | 430 | ||
| 436 | private void save() { | 431 | public interface SaveListener<T extends Entry> { |
| 437 | if (saveListener != null) { | 432 | void save(MemberMatches<T> matches); |
| 438 | saveListener.save(memberMatches); | 433 | } |
| 439 | } | ||
| 440 | } | ||
| 441 | } | 434 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java index bd9fe3d4..1fd2fa85 100644 --- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java +++ b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java | |||
| @@ -8,37 +8,37 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 14 | 15 | ||
| 15 | |||
| 16 | public class ScoredClassEntry extends ClassEntry { | 16 | public class ScoredClassEntry extends ClassEntry { |
| 17 | 17 | ||
| 18 | private static final long serialVersionUID = -8798725308554217105L; | 18 | private static final long serialVersionUID = -8798725308554217105L; |
| 19 | 19 | ||
| 20 | private float score; | 20 | private float score; |
| 21 | 21 | ||
| 22 | public ScoredClassEntry(ClassEntry other, float score) { | 22 | public ScoredClassEntry(ClassEntry other, float score) { |
| 23 | super(other); | 23 | super(other); |
| 24 | this.score = score; | 24 | this.score = score; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | public float getScore() { | 27 | public float getScore() { |
| 28 | return score; | 28 | return score; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | @Override | 31 | @Override |
| 32 | public int hashCode() { | 32 | public int hashCode() { |
| 33 | return Float.hashCode(score) + super.hashCode(); | 33 | return Float.hashCode(score) + super.hashCode(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | @Override | 36 | @Override |
| 37 | public boolean equals(Object other) { | 37 | public boolean equals(Object other) { |
| 38 | return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other); | 38 | return super.equals(other) && other instanceof ScoredClassEntry && equals((ScoredClassEntry) other); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | public boolean equals(ScoredClassEntry other) { | 41 | public boolean equals(ScoredClassEntry other) { |
| 42 | return other != null && score == other.score; | 42 | return other != null && Float.compare(score, other.score) == 0; |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java index 518055fd..7375111e 100644 --- a/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java | |||
| @@ -8,31 +8,28 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui; | ||
| 12 | |||
| 13 | import java.awt.Component; | ||
| 14 | 11 | ||
| 15 | import javax.swing.DefaultListCellRenderer; | 12 | package cuchaz.enigma.gui; |
| 16 | import javax.swing.JLabel; | ||
| 17 | import javax.swing.JList; | ||
| 18 | import javax.swing.ListCellRenderer; | ||
| 19 | 13 | ||
| 20 | import cuchaz.enigma.analysis.Token; | 14 | import cuchaz.enigma.analysis.Token; |
| 21 | 15 | ||
| 16 | import javax.swing.*; | ||
| 17 | import java.awt.*; | ||
| 18 | |||
| 22 | public class TokenListCellRenderer implements ListCellRenderer<Token> { | 19 | public class TokenListCellRenderer implements ListCellRenderer<Token> { |
| 23 | 20 | ||
| 24 | private GuiController controller; | 21 | private GuiController controller; |
| 25 | private DefaultListCellRenderer defaultRenderer; | 22 | private DefaultListCellRenderer defaultRenderer; |
| 26 | 23 | ||
| 27 | public TokenListCellRenderer(GuiController controller) { | 24 | public TokenListCellRenderer(GuiController controller) { |
| 28 | this.controller = controller; | 25 | this.controller = controller; |
| 29 | this.defaultRenderer = new DefaultListCellRenderer(); | 26 | this.defaultRenderer = new DefaultListCellRenderer(); |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 32 | @Override | 29 | @Override |
| 33 | public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) { | 30 | public Component getListCellRendererComponent(JList<? extends Token> list, Token token, int index, boolean isSelected, boolean hasFocus) { |
| 34 | JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); | 31 | JLabel label = (JLabel) this.defaultRenderer.getListCellRendererComponent(list, token, index, isSelected, hasFocus); |
| 35 | label.setText(this.controller.getReadableToken(token).toString()); | 32 | label.setText(this.controller.getReadableToken(token).toString()); |
| 36 | return label; | 33 | return label; |
| 37 | } | 34 | } |
| 38 | } | 35 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java index f690b159..7b3234d8 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java | |||
| @@ -8,63 +8,60 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | |||
| 13 | import java.awt.Color; | ||
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.Cursor; | ||
| 16 | import java.awt.FlowLayout; | ||
| 17 | import java.io.IOException; | ||
| 18 | 11 | ||
| 19 | import javax.swing.*; | 12 | package cuchaz.enigma.gui.dialog; |
| 20 | 13 | ||
| 21 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 22 | import cuchaz.enigma.utils.Utils; | 15 | import cuchaz.enigma.utils.Utils; |
| 23 | 16 | ||
| 17 | import javax.swing.*; | ||
| 18 | import java.awt.*; | ||
| 19 | import java.io.IOException; | ||
| 20 | |||
| 24 | public class AboutDialog { | 21 | public class AboutDialog { |
| 25 | 22 | ||
| 26 | public static void show(JFrame parent) { | 23 | public static void show(JFrame parent) { |
| 27 | // init frame | 24 | // init frame |
| 28 | final JFrame frame = new JFrame(Constants.NAME + " - About"); | 25 | final JFrame frame = new JFrame(Constants.NAME + " - About"); |
| 29 | final Container pane = frame.getContentPane(); | 26 | final Container pane = frame.getContentPane(); |
| 30 | pane.setLayout(new FlowLayout()); | 27 | pane.setLayout(new FlowLayout()); |
| 31 | 28 | ||
| 32 | // load the content | 29 | // load the content |
| 33 | try { | 30 | try { |
| 34 | String html = Utils.readResourceToString("/about.html"); | 31 | String html = Utils.readResourceToString("/about.html"); |
| 35 | html = String.format(html, Constants.NAME, Constants.VERSION); | 32 | html = String.format(html, Constants.NAME, Constants.VERSION); |
| 36 | JLabel label = new JLabel(html); | 33 | JLabel label = new JLabel(html); |
| 37 | label.setHorizontalAlignment(JLabel.CENTER); | 34 | label.setHorizontalAlignment(JLabel.CENTER); |
| 38 | pane.add(label); | 35 | pane.add(label); |
| 39 | } catch (IOException ex) { | 36 | } catch (IOException ex) { |
| 40 | throw new Error(ex); | 37 | throw new Error(ex); |
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | // show the link | 40 | // show the link |
| 44 | String html = "<html><a href=\"%s\">%s</a></html>"; | 41 | String html = "<html><a href=\"%s\">%s</a></html>"; |
| 45 | html = String.format(html, Constants.URL, Constants.URL); | 42 | html = String.format(html, Constants.URL, Constants.URL); |
| 46 | JButton link = new JButton(html); | 43 | JButton link = new JButton(html); |
| 47 | link.addActionListener(event -> Utils.openUrl(Constants.URL)); | 44 | link.addActionListener(event -> Utils.openUrl(Constants.URL)); |
| 48 | link.setBorderPainted(false); | 45 | link.setBorderPainted(false); |
| 49 | link.setOpaque(false); | 46 | link.setOpaque(false); |
| 50 | link.setBackground(Color.WHITE); | 47 | link.setBackground(Color.WHITE); |
| 51 | link.setCursor(new Cursor(Cursor.HAND_CURSOR)); | 48 | link.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| 52 | link.setFocusable(false); | 49 | link.setFocusable(false); |
| 53 | JPanel linkPanel = new JPanel(); | 50 | JPanel linkPanel = new JPanel(); |
| 54 | linkPanel.add(link); | 51 | linkPanel.add(link); |
| 55 | pane.add(linkPanel); | 52 | pane.add(linkPanel); |
| 56 | 53 | ||
| 57 | // show ok button | 54 | // show ok button |
| 58 | JButton okButton = new JButton("Ok"); | 55 | JButton okButton = new JButton("Ok"); |
| 59 | pane.add(okButton); | 56 | pane.add(okButton); |
| 60 | okButton.addActionListener(arg0 -> frame.dispose()); | 57 | okButton.addActionListener(arg0 -> frame.dispose()); |
| 61 | 58 | ||
| 62 | // show the frame | 59 | // show the frame |
| 63 | pane.doLayout(); | 60 | pane.doLayout(); |
| 64 | frame.setSize(400, 220); | 61 | frame.setSize(400, 220); |
| 65 | frame.setResizable(false); | 62 | frame.setResizable(false); |
| 66 | frame.setLocationRelativeTo(parent); | 63 | frame.setLocationRelativeTo(parent); |
| 67 | frame.setVisible(true); | 64 | frame.setVisible(true); |
| 68 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 65 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 69 | } | 66 | } |
| 70 | } | 67 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java index 5c1d9ff8..04dd5d7b 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java | |||
| @@ -8,80 +8,78 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | 11 | ||
| 13 | import java.awt.BorderLayout; | 12 | package cuchaz.enigma.gui.dialog; |
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.FlowLayout; | ||
| 16 | import java.io.PrintWriter; | ||
| 17 | import java.io.StringWriter; | ||
| 18 | |||
| 19 | import javax.swing.*; | ||
| 20 | 13 | ||
| 21 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 22 | import cuchaz.enigma.utils.Utils; | 15 | import cuchaz.enigma.utils.Utils; |
| 23 | 16 | ||
| 17 | import javax.swing.*; | ||
| 18 | import java.awt.*; | ||
| 19 | import java.io.PrintWriter; | ||
| 20 | import java.io.StringWriter; | ||
| 21 | |||
| 24 | public class CrashDialog { | 22 | public class CrashDialog { |
| 25 | 23 | ||
| 26 | private static CrashDialog instance = null; | 24 | private static CrashDialog instance = null; |
| 27 | 25 | ||
| 28 | private JFrame frame; | 26 | private JFrame frame; |
| 29 | private JTextArea text; | 27 | private JTextArea text; |
| 30 | 28 | ||
| 31 | private CrashDialog(JFrame parent) { | 29 | private CrashDialog(JFrame parent) { |
| 32 | // init frame | 30 | // init frame |
| 33 | frame = new JFrame(Constants.NAME + " - Crash Report"); | 31 | frame = new JFrame(Constants.NAME + " - Crash Report"); |
| 34 | final Container pane = frame.getContentPane(); | 32 | final Container pane = frame.getContentPane(); |
| 35 | pane.setLayout(new BorderLayout()); | 33 | pane.setLayout(new BorderLayout()); |
| 36 | 34 | ||
| 37 | JLabel label = new JLabel(Constants.NAME + " has crashed! =("); | 35 | JLabel label = new JLabel(Constants.NAME + " has crashed! =("); |
| 38 | label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | 36 | label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
| 39 | pane.add(label, BorderLayout.NORTH); | 37 | pane.add(label, BorderLayout.NORTH); |
| 40 | 38 | ||
| 41 | // report panel | 39 | // report panel |
| 42 | text = new JTextArea(); | 40 | text = new JTextArea(); |
| 43 | text.setTabSize(2); | 41 | text.setTabSize(2); |
| 44 | pane.add(new JScrollPane(text), BorderLayout.CENTER); | 42 | pane.add(new JScrollPane(text), BorderLayout.CENTER); |
| 45 | 43 | ||
| 46 | // buttons panel | 44 | // buttons panel |
| 47 | JPanel buttonsPanel = new JPanel(); | 45 | JPanel buttonsPanel = new JPanel(); |
| 48 | FlowLayout buttonsLayout = new FlowLayout(); | 46 | FlowLayout buttonsLayout = new FlowLayout(); |
| 49 | buttonsLayout.setAlignment(FlowLayout.RIGHT); | 47 | buttonsLayout.setAlignment(FlowLayout.RIGHT); |
| 50 | buttonsPanel.setLayout(buttonsLayout); | 48 | buttonsPanel.setLayout(buttonsLayout); |
| 51 | buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); | 49 | buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); |
| 52 | JButton ignoreButton = new JButton("Ignore"); | 50 | JButton ignoreButton = new JButton("Ignore"); |
| 53 | ignoreButton.addActionListener(event -> { | 51 | ignoreButton.addActionListener(event -> { |
| 54 | // close (hide) the dialog | 52 | // close (hide) the dialog |
| 55 | frame.setVisible(false); | 53 | frame.setVisible(false); |
| 56 | }); | 54 | }); |
| 57 | buttonsPanel.add(ignoreButton); | 55 | buttonsPanel.add(ignoreButton); |
| 58 | JButton exitButton = new JButton("Exit"); | 56 | JButton exitButton = new JButton("Exit"); |
| 59 | exitButton.addActionListener(event -> { | 57 | exitButton.addActionListener(event -> { |
| 60 | // exit enigma | 58 | // exit enigma |
| 61 | System.exit(1); | 59 | System.exit(1); |
| 62 | }); | 60 | }); |
| 63 | buttonsPanel.add(exitButton); | 61 | buttonsPanel.add(exitButton); |
| 64 | pane.add(buttonsPanel, BorderLayout.SOUTH); | 62 | pane.add(buttonsPanel, BorderLayout.SOUTH); |
| 65 | 63 | ||
| 66 | // show the frame | 64 | // show the frame |
| 67 | frame.setSize(600, 400); | 65 | frame.setSize(600, 400); |
| 68 | frame.setLocationRelativeTo(parent); | 66 | frame.setLocationRelativeTo(parent); |
| 69 | frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 67 | frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| 70 | } | 68 | } |
| 71 | 69 | ||
| 72 | public static void init(JFrame parent) { | 70 | public static void init(JFrame parent) { |
| 73 | instance = new CrashDialog(parent); | 71 | instance = new CrashDialog(parent); |
| 74 | } | 72 | } |
| 75 | 73 | ||
| 76 | public static void show(Throwable ex) { | 74 | public static void show(Throwable ex) { |
| 77 | // get the error report | 75 | // get the error report |
| 78 | StringWriter buf = new StringWriter(); | 76 | StringWriter buf = new StringWriter(); |
| 79 | ex.printStackTrace(new PrintWriter(buf)); | 77 | ex.printStackTrace(new PrintWriter(buf)); |
| 80 | String report = buf.toString(); | 78 | String report = buf.toString(); |
| 81 | 79 | ||
| 82 | // show it! | 80 | // show it! |
| 83 | instance.text.setText(report); | 81 | instance.text.setText(report); |
| 84 | instance.frame.doLayout(); | 82 | instance.frame.doLayout(); |
| 85 | instance.frame.setVisible(true); | 83 | instance.frame.setVisible(true); |
| 86 | } | 84 | } |
| 87 | } | 85 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java index 8df22a7f..5f048331 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java | |||
| @@ -8,92 +8,89 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.dialog; | ||
| 12 | |||
| 13 | import java.awt.BorderLayout; | ||
| 14 | import java.awt.Container; | ||
| 15 | import java.awt.Dimension; | ||
| 16 | import java.awt.FlowLayout; | ||
| 17 | 11 | ||
| 18 | import javax.swing.*; | 12 | package cuchaz.enigma.gui.dialog; |
| 19 | 13 | ||
| 20 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 21 | import cuchaz.enigma.Deobfuscator.ProgressListener; | 15 | import cuchaz.enigma.Deobfuscator.ProgressListener; |
| 22 | import cuchaz.enigma.utils.Utils; | 16 | import cuchaz.enigma.utils.Utils; |
| 23 | 17 | ||
| 18 | import javax.swing.*; | ||
| 19 | import java.awt.*; | ||
| 20 | |||
| 24 | public class ProgressDialog implements ProgressListener, AutoCloseable { | 21 | public class ProgressDialog implements ProgressListener, AutoCloseable { |
| 25 | 22 | ||
| 26 | private JFrame frame; | 23 | private JFrame frame; |
| 27 | private JLabel labelTitle; | 24 | private JLabel labelTitle; |
| 28 | private JLabel labelText; | 25 | private JLabel labelText; |
| 29 | private JProgressBar progress; | 26 | private JProgressBar progress; |
| 30 | 27 | ||
| 31 | public ProgressDialog(JFrame parent) { | 28 | public ProgressDialog(JFrame parent) { |
| 32 | 29 | ||
| 33 | // init frame | 30 | // init frame |
| 34 | this.frame = new JFrame(Constants.NAME + " - Operation in progress"); | 31 | this.frame = new JFrame(Constants.NAME + " - Operation in progress"); |
| 35 | final Container pane = this.frame.getContentPane(); | 32 | final Container pane = this.frame.getContentPane(); |
| 36 | FlowLayout layout = new FlowLayout(); | 33 | FlowLayout layout = new FlowLayout(); |
| 37 | layout.setAlignment(FlowLayout.LEFT); | 34 | layout.setAlignment(FlowLayout.LEFT); |
| 38 | pane.setLayout(layout); | 35 | pane.setLayout(layout); |
| 39 | 36 | ||
| 40 | this.labelTitle = new JLabel(); | 37 | this.labelTitle = new JLabel(); |
| 41 | pane.add(this.labelTitle); | 38 | pane.add(this.labelTitle); |
| 42 | 39 | ||
| 43 | // set up the progress bar | 40 | // set up the progress bar |
| 44 | JPanel panel = new JPanel(); | 41 | JPanel panel = new JPanel(); |
| 45 | pane.add(panel); | 42 | pane.add(panel); |
| 46 | panel.setLayout(new BorderLayout()); | 43 | panel.setLayout(new BorderLayout()); |
| 47 | this.labelText = Utils.unboldLabel(new JLabel()); | 44 | this.labelText = Utils.unboldLabel(new JLabel()); |
| 48 | this.progress = new JProgressBar(); | 45 | this.progress = new JProgressBar(); |
| 49 | this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); | 46 | this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); |
| 50 | panel.add(this.labelText, BorderLayout.NORTH); | 47 | panel.add(this.labelText, BorderLayout.NORTH); |
| 51 | panel.add(this.progress, BorderLayout.CENTER); | 48 | panel.add(this.progress, BorderLayout.CENTER); |
| 52 | panel.setPreferredSize(new Dimension(360, 50)); | 49 | panel.setPreferredSize(new Dimension(360, 50)); |
| 53 | 50 | ||
| 54 | // show the frame | 51 | // show the frame |
| 55 | pane.doLayout(); | 52 | pane.doLayout(); |
| 56 | this.frame.setSize(400, 120); | 53 | this.frame.setSize(400, 120); |
| 57 | this.frame.setResizable(false); | 54 | this.frame.setResizable(false); |
| 58 | this.frame.setLocationRelativeTo(parent); | 55 | this.frame.setLocationRelativeTo(parent); |
| 59 | this.frame.setVisible(true); | 56 | this.frame.setVisible(true); |
| 60 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); | 57 | this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); |
| 61 | } | 58 | } |
| 62 | 59 | ||
| 63 | public void close() { | 60 | public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { |
| 64 | this.frame.dispose(); | 61 | new Thread(() -> |
| 65 | } | 62 | { |
| 66 | 63 | try (ProgressDialog progress = new ProgressDialog(parent)) { | |
| 67 | @Override | 64 | runnable.run(progress); |
| 68 | public void init(int totalWork, String title) { | 65 | } catch (Exception ex) { |
| 69 | this.labelTitle.setText(title); | 66 | throw new Error(ex); |
| 70 | this.progress.setMinimum(0); | 67 | } |
| 71 | this.progress.setMaximum(totalWork); | 68 | }).start(); |
| 72 | this.progress.setValue(0); | 69 | } |
| 73 | } | 70 | |
| 74 | 71 | public void close() { | |
| 75 | @Override | 72 | this.frame.dispose(); |
| 76 | public void onProgress(int numDone, String message) { | 73 | } |
| 77 | this.labelText.setText(message); | 74 | |
| 78 | this.progress.setValue(numDone); | 75 | @Override |
| 79 | 76 | public void init(int totalWork, String title) { | |
| 80 | // update the frame | 77 | this.labelTitle.setText(title); |
| 81 | this.frame.validate(); | 78 | this.progress.setMinimum(0); |
| 82 | this.frame.repaint(); | 79 | this.progress.setMaximum(totalWork); |
| 83 | } | 80 | this.progress.setValue(0); |
| 84 | 81 | } | |
| 85 | public interface ProgressRunnable { | 82 | |
| 86 | void run(ProgressListener listener) throws Exception; | 83 | @Override |
| 87 | } | 84 | public void onProgress(int numDone, String message) { |
| 88 | 85 | this.labelText.setText(message); | |
| 89 | public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { | 86 | this.progress.setValue(numDone); |
| 90 | new Thread(() -> | 87 | |
| 91 | { | 88 | // update the frame |
| 92 | try (ProgressDialog progress = new ProgressDialog(parent)) { | 89 | this.frame.validate(); |
| 93 | runnable.run(progress); | 90 | this.frame.repaint(); |
| 94 | } catch (Exception ex) { | 91 | } |
| 95 | throw new Error(ex); | 92 | |
| 96 | } | 93 | public interface ProgressRunnable { |
| 97 | }).start(); | 94 | void run(ProgressListener listener) throws Exception; |
| 98 | } | 95 | } |
| 99 | } | 96 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index 0ccd5372..cd11acaf 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java | |||
| @@ -1,219 +1,207 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.gui.Gui; | ||
| 4 | import cuchaz.enigma.gui.dialog.AboutDialog; | ||
| 5 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 6 | |||
| 7 | import javax.swing.*; | ||
| 3 | import java.awt.event.InputEvent; | 8 | import java.awt.event.InputEvent; |
| 4 | import java.awt.event.KeyEvent; | 9 | import java.awt.event.KeyEvent; |
| 5 | import java.io.IOException; | 10 | import java.io.IOException; |
| 6 | import java.util.jar.JarFile; | 11 | import java.util.jar.JarFile; |
| 7 | 12 | ||
| 8 | import javax.swing.*; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.Gui; | ||
| 11 | import cuchaz.enigma.gui.dialog.AboutDialog; | ||
| 12 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 13 | |||
| 14 | public class MenuBar extends JMenuBar { | 13 | public class MenuBar extends JMenuBar { |
| 15 | 14 | ||
| 16 | private final Gui gui; | 15 | public final JMenuItem closeJarMenu; |
| 17 | 16 | public final JMenuItem openEnigmaMappingsMenu; | |
| 18 | public final JMenuItem closeJarMenu; | 17 | public final JMenuItem saveMappingsMenu; |
| 19 | 18 | public final JMenuItem saveMappingEnigmaFileMenu; | |
| 20 | public final JMenuItem openEnigmaMappingsMenu; | 19 | public final JMenuItem saveMappingEnigmaDirectoryMenu; |
| 21 | 20 | public final JMenuItem saveMappingsSrgMenu; | |
| 22 | public final JMenuItem saveMappingsMenu; | 21 | public final JMenuItem closeMappingsMenu; |
| 23 | public final JMenuItem saveMappingEnigmaFileMenu; | 22 | public final JMenuItem rebuildMethodNamesMenu; |
| 24 | public final JMenuItem saveMappingEnigmaDirectoryMenu; | 23 | public final JMenuItem exportSourceMenu; |
| 25 | public final JMenuItem saveMappingsSrgMenu; | 24 | public final JMenuItem exportJarMenu; |
| 26 | public final JMenuItem closeMappingsMenu; | 25 | private final Gui gui; |
| 27 | |||
| 28 | public final JMenuItem rebuildMethodNamesMenu; | ||
| 29 | |||
| 30 | public final JMenuItem exportSourceMenu; | ||
| 31 | public final JMenuItem exportJarMenu; | ||
| 32 | 26 | ||
| 33 | public MenuBar(Gui gui) { | 27 | public MenuBar(Gui gui) { |
| 34 | this.gui = gui; | 28 | this.gui = gui; |
| 35 | 29 | ||
| 36 | { | 30 | { |
| 37 | JMenu menu = new JMenu("File"); | 31 | JMenu menu = new JMenu("File"); |
| 38 | this.add(menu); | 32 | this.add(menu); |
| 39 | { | 33 | { |
| 40 | JMenuItem item = new JMenuItem("Open Jar..."); | 34 | JMenuItem item = new JMenuItem("Open Jar..."); |
| 41 | menu.add(item); | 35 | menu.add(item); |
| 42 | item.addActionListener(event -> { | 36 | item.addActionListener(event -> { |
| 43 | if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 37 | if (this.gui.jarFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 44 | // load the jar in a separate thread | 38 | // load the jar in a separate thread |
| 45 | new Thread(() -> | 39 | new Thread(() -> |
| 46 | { | 40 | { |
| 47 | try { | 41 | try { |
| 48 | gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile())); | 42 | gui.getController().openJar(new JarFile(gui.jarFileChooser.getSelectedFile())); |
| 49 | } catch (IOException ex) { | 43 | } catch (IOException ex) { |
| 50 | throw new Error(ex); | 44 | throw new Error(ex); |
| 51 | } | 45 | } |
| 52 | }).start(); | 46 | }).start(); |
| 53 | } | 47 | } |
| 54 | }); | 48 | }); |
| 55 | } | 49 | } |
| 56 | { | 50 | { |
| 57 | JMenuItem item = new JMenuItem("Close Jar"); | 51 | JMenuItem item = new JMenuItem("Close Jar"); |
| 58 | menu.add(item); | 52 | menu.add(item); |
| 59 | item.addActionListener(event -> this.gui.getController().closeJar()); | 53 | item.addActionListener(event -> this.gui.getController().closeJar()); |
| 60 | this.closeJarMenu = item; | 54 | this.closeJarMenu = item; |
| 61 | } | 55 | } |
| 62 | menu.addSeparator(); | 56 | menu.addSeparator(); |
| 63 | JMenu openMenu = new JMenu("Open Mappings..."); | 57 | JMenu openMenu = new JMenu("Open Mappings..."); |
| 64 | menu.add(openMenu); | 58 | menu.add(openMenu); |
| 65 | { | 59 | { |
| 66 | JMenuItem item = new JMenuItem("Enigma"); | 60 | JMenuItem item = new JMenuItem("Enigma"); |
| 67 | openMenu.add(item); | 61 | openMenu.add(item); |
| 68 | item.addActionListener(event -> { | 62 | item.addActionListener(event -> { |
| 69 | if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 63 | if (this.gui.enigmaMappingsFileChooser.showOpenDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 70 | try { | 64 | try { |
| 71 | this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 65 | this.gui.getController().openEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 72 | } catch (IOException ex) { | 66 | } catch (IOException ex) { |
| 73 | throw new Error(ex); | 67 | throw new Error(ex); |
| 74 | } catch (MappingParseException ex) { | 68 | } catch (MappingParseException ex) { |
| 75 | JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); | 69 | JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage()); |
| 76 | } | 70 | } |
| 77 | } | 71 | } |
| 78 | }); | 72 | }); |
| 79 | this.openEnigmaMappingsMenu = item; | 73 | this.openEnigmaMappingsMenu = item; |
| 80 | } | 74 | } |
| 81 | { | 75 | { |
| 82 | JMenuItem item = new JMenuItem("Save Mappings"); | 76 | JMenuItem item = new JMenuItem("Save Mappings"); |
| 83 | menu.add(item); | 77 | menu.add(item); |
| 84 | item.addActionListener(event -> { | 78 | item.addActionListener(event -> { |
| 85 | try { | 79 | try { |
| 86 | this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 80 | this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 87 | } catch (IOException ex) { | 81 | } catch (IOException ex) { |
| 88 | throw new Error(ex); | 82 | throw new Error(ex); |
| 89 | } | 83 | } |
| 90 | }); | 84 | }); |
| 91 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); | 85 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); |
| 92 | this.saveMappingsMenu = item; | 86 | this.saveMappingsMenu = item; |
| 93 | } | 87 | } |
| 94 | JMenu saveMenu = new JMenu("Save Mappings As..."); | 88 | JMenu saveMenu = new JMenu("Save Mappings As..."); |
| 95 | menu.add(saveMenu); | 89 | menu.add(saveMenu); |
| 96 | { | 90 | { |
| 97 | JMenuItem item = new JMenuItem("Enigma (single file)"); | 91 | JMenuItem item = new JMenuItem("Enigma (single file)"); |
| 98 | saveMenu.add(item); | 92 | saveMenu.add(item); |
| 99 | item.addActionListener(event -> { | 93 | item.addActionListener(event -> { |
| 100 | // TODO: Use a specific file chooser for it | 94 | // TODO: Use a specific file chooser for it |
| 101 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 95 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 102 | try { | 96 | try { |
| 103 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); | 97 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), false); |
| 104 | this.saveMappingsMenu.setEnabled(true); | 98 | this.saveMappingsMenu.setEnabled(true); |
| 105 | } catch (IOException ex) { | 99 | } catch (IOException ex) { |
| 106 | throw new Error(ex); | 100 | throw new Error(ex); |
| 107 | } | 101 | } |
| 108 | } | 102 | } |
| 109 | }); | 103 | }); |
| 110 | this.saveMappingEnigmaFileMenu = item; | 104 | this.saveMappingEnigmaFileMenu = item; |
| 111 | } | 105 | } |
| 112 | { | 106 | { |
| 113 | JMenuItem item = new JMenuItem("Enigma (directory)"); | 107 | JMenuItem item = new JMenuItem("Enigma (directory)"); |
| 114 | saveMenu.add(item); | 108 | saveMenu.add(item); |
| 115 | item.addActionListener(event -> { | 109 | item.addActionListener(event -> { |
| 116 | // TODO: Use a specific file chooser for it | 110 | // TODO: Use a specific file chooser for it |
| 117 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 111 | if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 118 | try { | 112 | try { |
| 119 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); | 113 | this.gui.getController().saveEnigmaMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile(), true); |
| 120 | this.saveMappingsMenu.setEnabled(true); | 114 | this.saveMappingsMenu.setEnabled(true); |
| 121 | } catch (IOException ex) { | 115 | } catch (IOException ex) { |
| 122 | throw new Error(ex); | 116 | throw new Error(ex); |
| 123 | } | 117 | } |
| 124 | } | 118 | } |
| 125 | }); | 119 | }); |
| 126 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); | 120 | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); |
| 127 | this.saveMappingEnigmaDirectoryMenu = item; | 121 | this.saveMappingEnigmaDirectoryMenu = item; |
| 128 | } | 122 | } |
| 129 | { | 123 | { |
| 130 | JMenuItem item = new JMenuItem("SRG (single file)"); | 124 | JMenuItem item = new JMenuItem("SRG (single file)"); |
| 131 | saveMenu.add(item); | 125 | saveMenu.add(item); |
| 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 | try { |
| 136 | this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); | 130 | this.gui.getController().saveSRGMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile()); |
| 137 | this.saveMappingsMenu.setEnabled(true); | 131 | this.saveMappingsMenu.setEnabled(true); |
| 138 | } catch (IOException ex) { | 132 | } catch (IOException ex) { |
| 139 | throw new Error(ex); | 133 | throw new Error(ex); |
| 140 | } | 134 | } |
| 141 | } | 135 | } |
| 142 | }); | 136 | }); |
| 143 | this.saveMappingsSrgMenu = item; | 137 | this.saveMappingsSrgMenu = item; |
| 144 | } | 138 | } |
| 145 | { | 139 | { |
| 146 | JMenuItem item = new JMenuItem("Close Mappings"); | 140 | JMenuItem item = new JMenuItem("Close Mappings"); |
| 147 | menu.add(item); | 141 | menu.add(item); |
| 148 | item.addActionListener(event -> { | 142 | item.addActionListener(event -> { |
| 149 | if (this.gui.getController().isDirty()) | 143 | if (this.gui.getController().isDirty()) { |
| 150 | { | 144 | this.gui.showDiscardDiag((response -> { |
| 151 | this.gui.showDiscardDiag((response -> { | 145 | if (response == JOptionPane.YES_OPTION) { |
| 152 | if (response == JOptionPane.YES_OPTION) | 146 | try { |
| 153 | { | 147 | gui.saveMapping(); |
| 154 | try | 148 | this.gui.getController().closeMappings(); |
| 155 | { | 149 | } catch (IOException e) { |
| 156 | gui.saveMapping(); | 150 | throw new Error(e); |
| 157 | this.gui.getController().closeMappings(); | 151 | } |
| 158 | } catch (IOException e) | 152 | } else if (response == JOptionPane.NO_OPTION) |
| 159 | { | 153 | this.gui.getController().closeMappings(); |
| 160 | throw new Error(e); | 154 | return null; |
| 161 | } | 155 | }), "Save and close", "Discard changes", "Cancel"); |
| 162 | } | 156 | } else |
| 163 | else if (response == JOptionPane.NO_OPTION) | 157 | this.gui.getController().closeMappings(); |
| 164 | this.gui.getController().closeMappings(); | ||
| 165 | return null; | ||
| 166 | }), "Save and close", "Discard changes", "Cancel"); | ||
| 167 | } | ||
| 168 | else | ||
| 169 | this.gui.getController().closeMappings(); | ||
| 170 | 158 | ||
| 171 | }); | 159 | }); |
| 172 | this.closeMappingsMenu = item; | 160 | this.closeMappingsMenu = item; |
| 173 | } | 161 | } |
| 174 | menu.addSeparator(); | 162 | menu.addSeparator(); |
| 175 | { | 163 | { |
| 176 | JMenuItem item = new JMenuItem("Rebuild Method Names"); | 164 | JMenuItem item = new JMenuItem("Rebuild Method Names"); |
| 177 | menu.add(item); | 165 | menu.add(item); |
| 178 | item.addActionListener(event -> this.gui.getController().rebuildMethodNames()); | 166 | item.addActionListener(event -> this.gui.getController().rebuildMethodNames()); |
| 179 | this.rebuildMethodNamesMenu = item; | 167 | this.rebuildMethodNamesMenu = item; |
| 180 | } | 168 | } |
| 181 | menu.addSeparator(); | 169 | menu.addSeparator(); |
| 182 | { | 170 | { |
| 183 | JMenuItem item = new JMenuItem("Export Source..."); | 171 | JMenuItem item = new JMenuItem("Export Source..."); |
| 184 | menu.add(item); | 172 | menu.add(item); |
| 185 | item.addActionListener(event -> { | 173 | item.addActionListener(event -> { |
| 186 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 174 | if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 187 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); | 175 | this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); |
| 188 | } | 176 | } |
| 189 | }); | 177 | }); |
| 190 | this.exportSourceMenu = item; | 178 | this.exportSourceMenu = item; |
| 191 | } | 179 | } |
| 192 | { | 180 | { |
| 193 | JMenuItem item = new JMenuItem("Export Jar..."); | 181 | JMenuItem item = new JMenuItem("Export Jar..."); |
| 194 | menu.add(item); | 182 | menu.add(item); |
| 195 | item.addActionListener(event -> { | 183 | item.addActionListener(event -> { |
| 196 | if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { | 184 | if (this.gui.exportJarFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { |
| 197 | this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile()); | 185 | this.gui.getController().exportJar(this.gui.exportJarFileChooser.getSelectedFile()); |
| 198 | } | 186 | } |
| 199 | }); | 187 | }); |
| 200 | this.exportJarMenu = item; | 188 | this.exportJarMenu = item; |
| 201 | } | 189 | } |
| 202 | menu.addSeparator(); | 190 | menu.addSeparator(); |
| 203 | { | 191 | { |
| 204 | JMenuItem item = new JMenuItem("Exit"); | 192 | JMenuItem item = new JMenuItem("Exit"); |
| 205 | menu.add(item); | 193 | menu.add(item); |
| 206 | item.addActionListener(event -> this.gui.close()); | 194 | item.addActionListener(event -> this.gui.close()); |
| 207 | } | 195 | } |
| 208 | } | 196 | } |
| 209 | { | 197 | { |
| 210 | JMenu menu = new JMenu("Help"); | 198 | JMenu menu = new JMenu("Help"); |
| 211 | this.add(menu); | 199 | this.add(menu); |
| 212 | { | 200 | { |
| 213 | JMenuItem item = new JMenuItem("About"); | 201 | JMenuItem item = new JMenuItem("About"); |
| 214 | menu.add(item); | 202 | menu.add(item); |
| 215 | item.addActionListener(event -> AboutDialog.show(this.gui.getFrame())); | 203 | item.addActionListener(event -> AboutDialog.show(this.gui.getFrame())); |
| 216 | } | 204 | } |
| 217 | } | 205 | } |
| 218 | } | 206 | } |
| 219 | } | 207 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java index e387ed31..2f6d96c4 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java | |||
| @@ -1,79 +1,76 @@ | |||
| 1 | package cuchaz.enigma.gui.elements; | 1 | package cuchaz.enigma.gui.elements; |
| 2 | 2 | ||
| 3 | import java.awt.event.KeyEvent; | ||
| 4 | |||
| 5 | import javax.swing.JMenuItem; | ||
| 6 | import javax.swing.JPopupMenu; | ||
| 7 | import javax.swing.KeyStroke; | ||
| 8 | |||
| 9 | import cuchaz.enigma.gui.Gui; | 3 | import cuchaz.enigma.gui.Gui; |
| 10 | 4 | ||
| 5 | import javax.swing.*; | ||
| 6 | import java.awt.event.KeyEvent; | ||
| 7 | |||
| 11 | public class PopupMenuBar extends JPopupMenu { | 8 | public class PopupMenuBar extends JPopupMenu { |
| 12 | 9 | ||
| 13 | public final JMenuItem renameMenu; | 10 | public final JMenuItem renameMenu; |
| 14 | public final JMenuItem showInheritanceMenu; | 11 | public final JMenuItem showInheritanceMenu; |
| 15 | public final JMenuItem showImplementationsMenu; | 12 | public final JMenuItem showImplementationsMenu; |
| 16 | public final JMenuItem showCallsMenu; | 13 | public final JMenuItem showCallsMenu; |
| 17 | public final JMenuItem openEntryMenu; | 14 | public final JMenuItem openEntryMenu; |
| 18 | public final JMenuItem openPreviousMenu; | 15 | public final JMenuItem openPreviousMenu; |
| 19 | public final JMenuItem toggleMappingMenu; | 16 | public final JMenuItem toggleMappingMenu; |
| 20 | 17 | ||
| 21 | public PopupMenuBar(Gui gui) { | 18 | public PopupMenuBar(Gui gui) { |
| 22 | { | 19 | { |
| 23 | JMenuItem menu = new JMenuItem("Rename"); | 20 | JMenuItem menu = new JMenuItem("Rename"); |
| 24 | menu.addActionListener(event -> gui.startRename()); | 21 | menu.addActionListener(event -> gui.startRename()); |
| 25 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); | 22 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0)); |
| 26 | menu.setEnabled(false); | 23 | menu.setEnabled(false); |
| 27 | this.add(menu); | 24 | this.add(menu); |
| 28 | this.renameMenu = menu; | 25 | this.renameMenu = menu; |
| 29 | } | 26 | } |
| 30 | { | 27 | { |
| 31 | JMenuItem menu = new JMenuItem("Show Inheritance"); | 28 | JMenuItem menu = new JMenuItem("Show Inheritance"); |
| 32 | menu.addActionListener(event -> gui.showInheritance()); | 29 | menu.addActionListener(event -> gui.showInheritance()); |
| 33 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); | 30 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0)); |
| 34 | menu.setEnabled(false); | 31 | menu.setEnabled(false); |
| 35 | this.add(menu); | 32 | this.add(menu); |
| 36 | this.showInheritanceMenu = menu; | 33 | this.showInheritanceMenu = menu; |
| 37 | } | 34 | } |
| 38 | { | 35 | { |
| 39 | JMenuItem menu = new JMenuItem("Show Implementations"); | 36 | JMenuItem menu = new JMenuItem("Show Implementations"); |
| 40 | menu.addActionListener(event -> gui.showImplementations()); | 37 | menu.addActionListener(event -> gui.showImplementations()); |
| 41 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); | 38 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0)); |
| 42 | menu.setEnabled(false); | 39 | menu.setEnabled(false); |
| 43 | this.add(menu); | 40 | this.add(menu); |
| 44 | this.showImplementationsMenu = menu; | 41 | this.showImplementationsMenu = menu; |
| 45 | } | 42 | } |
| 46 | { | 43 | { |
| 47 | JMenuItem menu = new JMenuItem("Show Calls"); | 44 | JMenuItem menu = new JMenuItem("Show Calls"); |
| 48 | menu.addActionListener(event -> gui.showCalls()); | 45 | menu.addActionListener(event -> gui.showCalls()); |
| 49 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); | 46 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0)); |
| 50 | menu.setEnabled(false); | 47 | menu.setEnabled(false); |
| 51 | this.add(menu); | 48 | this.add(menu); |
| 52 | this.showCallsMenu = menu; | 49 | this.showCallsMenu = menu; |
| 53 | } | 50 | } |
| 54 | { | 51 | { |
| 55 | JMenuItem menu = new JMenuItem("Go to Declaration"); | 52 | JMenuItem menu = new JMenuItem("Go to Declaration"); |
| 56 | menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); | 53 | menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); |
| 57 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); | 54 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); |
| 58 | menu.setEnabled(false); | 55 | menu.setEnabled(false); |
| 59 | this.add(menu); | 56 | this.add(menu); |
| 60 | this.openEntryMenu = menu; | 57 | this.openEntryMenu = menu; |
| 61 | } | 58 | } |
| 62 | { | 59 | { |
| 63 | JMenuItem menu = new JMenuItem("Go to previous"); | 60 | JMenuItem menu = new JMenuItem("Go to previous"); |
| 64 | menu.addActionListener(event -> gui.getController().openPreviousReference()); | 61 | menu.addActionListener(event -> gui.getController().openPreviousReference()); |
| 65 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); | 62 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); |
| 66 | menu.setEnabled(false); | 63 | menu.setEnabled(false); |
| 67 | this.add(menu); | 64 | this.add(menu); |
| 68 | this.openPreviousMenu = menu; | 65 | this.openPreviousMenu = menu; |
| 69 | } | 66 | } |
| 70 | { | 67 | { |
| 71 | JMenuItem menu = new JMenuItem("Mark as deobfuscated"); | 68 | JMenuItem menu = new JMenuItem("Mark as deobfuscated"); |
| 72 | menu.addActionListener(event -> gui.toggleMapping()); | 69 | menu.addActionListener(event -> gui.toggleMapping()); |
| 73 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); | 70 | menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); |
| 74 | menu.setEnabled(false); | 71 | menu.setEnabled(false); |
| 75 | this.add(menu); | 72 | this.add(menu); |
| 76 | this.toggleMappingMenu = menu; | 73 | this.toggleMappingMenu = menu; |
| 77 | } | 74 | } |
| 78 | } | 75 | } |
| 79 | } | 76 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java index 23393347..f5f66287 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserAny.java | |||
| @@ -2,10 +2,9 @@ package cuchaz.enigma.gui.filechooser; | |||
| 2 | 2 | ||
| 3 | import javax.swing.*; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserAny extends JFileChooser | 5 | public class FileChooserAny extends JFileChooser { |
| 6 | { | 6 | public FileChooserAny() { |
| 7 | public FileChooserAny() { | 7 | this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); |
| 8 | this.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); | 8 | this.setAcceptAllFileFilterUsed(false); |
| 9 | this.setAcceptAllFileFilterUsed(false); | 9 | } |
| 10 | } | ||
| 11 | } \ No newline at end of file | 10 | } \ No newline at end of file |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java index 62a0f200..cea11a68 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFile.java | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | package cuchaz.enigma.gui.filechooser; | 1 | package cuchaz.enigma.gui.filechooser; |
| 2 | 2 | ||
| 3 | import javax.swing.JFileChooser; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserFile extends JFileChooser { | 5 | public class FileChooserFile extends JFileChooser { |
| 6 | public FileChooserFile() { | 6 | public FileChooserFile() { |
| 7 | } | 7 | } |
| 8 | } | 8 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java index 93ca5d68..c16e0afc 100644 --- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java +++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | package cuchaz.enigma.gui.filechooser; | 1 | package cuchaz.enigma.gui.filechooser; |
| 2 | 2 | ||
| 3 | import javax.swing.JFileChooser; | 3 | import javax.swing.*; |
| 4 | 4 | ||
| 5 | public class FileChooserFolder extends JFileChooser { | 5 | public class FileChooserFolder extends JFileChooser { |
| 6 | 6 | ||
| 7 | public FileChooserFolder() { | 7 | public FileChooserFolder() { |
| 8 | this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); | 8 | this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); |
| 9 | this.setAcceptAllFileFilterUsed(false); | 9 | this.setAcceptAllFileFilterUsed(false); |
| 10 | } | 10 | } |
| 11 | } | 11 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 0a730880..0f649278 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java | |||
| @@ -8,57 +8,54 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.highlight; | ||
| 12 | 11 | ||
| 13 | import java.awt.Color; | 12 | package cuchaz.enigma.gui.highlight; |
| 14 | import java.awt.Graphics; | ||
| 15 | import java.awt.Rectangle; | ||
| 16 | import java.awt.Shape; | ||
| 17 | 13 | ||
| 18 | import javax.swing.text.BadLocationException; | 14 | import javax.swing.text.BadLocationException; |
| 19 | import javax.swing.text.Highlighter; | 15 | import javax.swing.text.Highlighter; |
| 20 | import javax.swing.text.JTextComponent; | 16 | import javax.swing.text.JTextComponent; |
| 17 | import java.awt.*; | ||
| 21 | 18 | ||
| 22 | public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { | 19 | public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { |
| 23 | 20 | ||
| 24 | private Color fillColor; | 21 | private Color fillColor; |
| 25 | private Color borderColor; | 22 | private Color borderColor; |
| 26 | 23 | ||
| 27 | protected BoxHighlightPainter(Color fillColor, Color borderColor) { | 24 | protected BoxHighlightPainter(Color fillColor, Color borderColor) { |
| 28 | this.fillColor = fillColor; | 25 | this.fillColor = fillColor; |
| 29 | this.borderColor = borderColor; | 26 | this.borderColor = borderColor; |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 32 | @Override | 29 | public static Rectangle getBounds(JTextComponent text, int start, int end) { |
| 33 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | 30 | try { |
| 34 | Rectangle bounds = getBounds(text, start, end); | 31 | // determine the bounds of the text |
| 35 | 32 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | |
| 36 | // fill the area | 33 | |
| 37 | if (this.fillColor != null) { | 34 | // adjust the box so it looks nice |
| 38 | g.setColor(this.fillColor); | 35 | bounds.x -= 2; |
| 39 | g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 36 | bounds.width += 2; |
| 40 | } | 37 | bounds.y += 1; |
| 41 | 38 | bounds.height -= 2; | |
| 42 | // draw a box around the area | 39 | |
| 43 | g.setColor(this.borderColor); | 40 | return bounds; |
| 44 | g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 41 | } catch (BadLocationException ex) { |
| 45 | } | 42 | // don't care... just return something |
| 46 | 43 | return new Rectangle(0, 0, 0, 0); | |
| 47 | public static Rectangle getBounds(JTextComponent text, int start, int end) { | 44 | } |
| 48 | try { | 45 | } |
| 49 | // determine the bounds of the text | 46 | |
| 50 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | 47 | @Override |
| 51 | 48 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | |
| 52 | // adjust the box so it looks nice | 49 | Rectangle bounds = getBounds(text, start, end); |
| 53 | bounds.x -= 2; | 50 | |
| 54 | bounds.width += 2; | 51 | // fill the area |
| 55 | bounds.y += 1; | 52 | if (this.fillColor != null) { |
| 56 | bounds.height -= 2; | 53 | g.setColor(this.fillColor); |
| 57 | 54 | g.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | |
| 58 | return bounds; | 55 | } |
| 59 | } catch (BadLocationException ex) { | 56 | |
| 60 | // don't care... just return something | 57 | // draw a box around the area |
| 61 | return new Rectangle(0, 0, 0, 0); | 58 | g.setColor(this.borderColor); |
| 62 | } | 59 | g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); |
| 63 | } | 60 | } |
| 64 | } | 61 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java index 5d572030..a2d28844 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { | 16 | public class DeobfuscatedHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public DeobfuscatedHighlightPainter() { | 18 | public DeobfuscatedHighlightPainter() { |
| 18 | super(new Color(220, 255, 220), new Color(80, 160, 80)); | 19 | super(new Color(220, 255, 220), new Color(80, 160, 80)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java index ee64d86a..0947d4b7 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class ObfuscatedHighlightPainter extends BoxHighlightPainter { | 16 | public class ObfuscatedHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public ObfuscatedHighlightPainter() { | 18 | public ObfuscatedHighlightPainter() { |
| 18 | super(new Color(255, 220, 220), new Color(160, 80, 80)); | 19 | super(new Color(255, 220, 220), new Color(160, 80, 80)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java index 43d8352e..74e7273d 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.highlight; | 12 | package cuchaz.enigma.gui.highlight; |
| 12 | 13 | ||
| 13 | import java.awt.Color; | 14 | import java.awt.*; |
| 14 | 15 | ||
| 15 | public class OtherHighlightPainter extends BoxHighlightPainter { | 16 | public class OtherHighlightPainter extends BoxHighlightPainter { |
| 16 | 17 | ||
| 17 | public OtherHighlightPainter() { | 18 | public OtherHighlightPainter() { |
| 18 | super(null, new Color(180, 180, 180)); | 19 | super(null, new Color(180, 180, 180)); |
| 19 | } | 20 | } |
| 20 | } | 21 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java index f772284f..5cbeabc0 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java | |||
| @@ -8,22 +8,22 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.highlight; | ||
| 12 | 11 | ||
| 13 | import java.awt.*; | 12 | package cuchaz.enigma.gui.highlight; |
| 14 | 13 | ||
| 15 | import javax.swing.text.Highlighter; | 14 | import javax.swing.text.Highlighter; |
| 16 | import javax.swing.text.JTextComponent; | 15 | import javax.swing.text.JTextComponent; |
| 16 | import java.awt.*; | ||
| 17 | 17 | ||
| 18 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { | 18 | public class SelectionHighlightPainter implements Highlighter.HighlightPainter { |
| 19 | 19 | ||
| 20 | @Override | 20 | @Override |
| 21 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { | 21 | public void paint(Graphics g, int start, int end, Shape shape, JTextComponent text) { |
| 22 | // draw a thick border | 22 | // draw a thick border |
| 23 | Graphics2D g2d = (Graphics2D) g; | 23 | Graphics2D g2d = (Graphics2D) g; |
| 24 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); | 24 | Rectangle bounds = BoxHighlightPainter.getBounds(text, start, end); |
| 25 | g2d.setColor(Color.black); | 25 | g2d.setColor(Color.black); |
| 26 | g2d.setStroke(new BasicStroke(2.0f)); | 26 | g2d.setStroke(new BasicStroke(2.0f)); |
| 27 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); | 27 | g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 9f391f2e..dc933cd5 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java | |||
| @@ -8,58 +8,59 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.gui.node; | ||
| 12 | 11 | ||
| 13 | import javax.swing.tree.DefaultMutableTreeNode; | 12 | package cuchaz.enigma.gui.node; |
| 14 | 13 | ||
| 15 | import cuchaz.enigma.mapping.ClassEntry; | 14 | import cuchaz.enigma.mapping.ClassEntry; |
| 16 | 15 | ||
| 16 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 17 | |||
| 17 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { |
| 18 | 19 | ||
| 19 | private ClassEntry classEntry; | 20 | private ClassEntry classEntry; |
| 20 | 21 | ||
| 21 | public ClassSelectorClassNode(ClassEntry classEntry) { | 22 | public ClassSelectorClassNode(ClassEntry classEntry) { |
| 22 | this.classEntry = classEntry; | 23 | this.classEntry = classEntry; |
| 23 | this.setUserObject(classEntry); | 24 | this.setUserObject(classEntry); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public ClassEntry getClassEntry() { | 27 | public ClassEntry getClassEntry() { |
| 27 | return this.classEntry; | 28 | return this.classEntry; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | @Override | 31 | @Override |
| 31 | public String toString() { | 32 | public String toString() { |
| 32 | return this.classEntry.getSimpleName(); | 33 | return this.classEntry.getSimpleName(); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | @Override | 36 | @Override |
| 36 | public boolean equals(Object other) { | 37 | public boolean equals(Object other) { |
| 37 | return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other); | 38 | return other instanceof ClassSelectorClassNode && equals((ClassSelectorClassNode) other); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | @Override public int hashCode() | 41 | @Override |
| 41 | { | 42 | public int hashCode() { |
| 42 | return 17 + (classEntry != null ? classEntry.hashCode() : 0); | 43 | return 17 + (classEntry != null ? classEntry.hashCode() : 0); |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | @Override public void setUserObject(Object userObject) | 46 | @Override |
| 46 | { | 47 | public Object getUserObject() { |
| 47 | String packageName = ""; | 48 | return classEntry; |
| 48 | if (classEntry.getPackageName() != null) | 49 | } |
| 49 | packageName = classEntry.getPackageName() + "/"; | ||
| 50 | if (userObject instanceof String) | ||
| 51 | this.classEntry = new ClassEntry(packageName + userObject); | ||
| 52 | else if (userObject instanceof ClassEntry) | ||
| 53 | this.classEntry = (ClassEntry) userObject; | ||
| 54 | super.setUserObject(classEntry); | ||
| 55 | } | ||
| 56 | 50 | ||
| 57 | @Override public Object getUserObject() | 51 | @Override |
| 58 | { | 52 | public void setUserObject(Object userObject) { |
| 59 | return classEntry; | 53 | String packageName = ""; |
| 60 | } | 54 | if (classEntry.getPackageName() != null) |
| 55 | packageName = classEntry.getPackageName() + "/"; | ||
| 56 | if (userObject instanceof String) | ||
| 57 | this.classEntry = new ClassEntry(packageName + userObject); | ||
| 58 | else if (userObject instanceof ClassEntry) | ||
| 59 | this.classEntry = (ClassEntry) userObject; | ||
| 60 | super.setUserObject(classEntry); | ||
| 61 | } | ||
| 61 | 62 | ||
| 62 | public boolean equals(ClassSelectorClassNode other) { | 63 | public boolean equals(ClassSelectorClassNode other) { |
| 63 | return this.classEntry.equals(other.classEntry); | 64 | return this.classEntry.equals(other.classEntry); |
| 64 | } | 65 | } |
| 65 | } | 66 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java index b3eb90ef..f80abba6 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.gui.node; | 12 | package cuchaz.enigma.gui.node; |
| 12 | 13 | ||
| 13 | import javassist.bytecode.Descriptor; | 14 | import javassist.bytecode.Descriptor; |
| @@ -16,44 +17,44 @@ import javax.swing.tree.DefaultMutableTreeNode; | |||
| 16 | 17 | ||
| 17 | public class ClassSelectorPackageNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorPackageNode extends DefaultMutableTreeNode { |
| 18 | 19 | ||
| 19 | private String packageName; | 20 | private String packageName; |
| 20 | 21 | ||
| 21 | public ClassSelectorPackageNode(String packageName) { | 22 | public ClassSelectorPackageNode(String packageName) { |
| 22 | this.packageName = packageName != null ? packageName : "(none)"; | 23 | this.packageName = packageName != null ? packageName : "(none)"; |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | public String getPackageName() { | 26 | public String getPackageName() { |
| 26 | return packageName; | 27 | return packageName; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | @Override public void setUserObject(Object userObject) | 30 | @Override |
| 30 | { | 31 | public Object getUserObject() { |
| 31 | if (userObject instanceof String) | 32 | return packageName; |
| 32 | this.packageName = (String) userObject; | 33 | } |
| 33 | super.setUserObject(userObject); | 34 | |
| 34 | } | 35 | @Override |
| 35 | 36 | public void setUserObject(Object userObject) { | |
| 36 | @Override public Object getUserObject() | 37 | if (userObject instanceof String) |
| 37 | { | 38 | this.packageName = (String) userObject; |
| 38 | return packageName; | 39 | super.setUserObject(userObject); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | @Override | 42 | @Override |
| 42 | public String toString() { | 43 | public String toString() { |
| 43 | return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; | 44 | return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)"; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | @Override | 47 | @Override |
| 47 | public boolean equals(Object other) { | 48 | public boolean equals(Object other) { |
| 48 | return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other); | 49 | return other instanceof ClassSelectorPackageNode && equals((ClassSelectorPackageNode) other); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | @Override public int hashCode() | 52 | @Override |
| 52 | { | 53 | public int hashCode() { |
| 53 | return packageName.hashCode(); | 54 | return packageName.hashCode(); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | public boolean equals(ClassSelectorPackageNode other) { | 57 | public boolean equals(ClassSelectorPackageNode other) { |
| 57 | return other != null && this.packageName.equals(other.packageName); | 58 | return other != null && this.packageName.equals(other.packageName); |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java index 4f551756..68cc8e14 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java | |||
| @@ -1,28 +1,25 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | ||
| 4 | |||
| 5 | import javax.swing.JLabel; | ||
| 6 | import javax.swing.JPanel; | ||
| 7 | import javax.swing.JScrollPane; | ||
| 8 | |||
| 9 | import cuchaz.enigma.gui.ClassSelector; | 3 | import cuchaz.enigma.gui.ClassSelector; |
| 10 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 11 | 5 | ||
| 6 | import javax.swing.*; | ||
| 7 | import java.awt.*; | ||
| 8 | |||
| 12 | public class PanelDeobf extends JPanel { | 9 | public class PanelDeobf extends JPanel { |
| 13 | 10 | ||
| 14 | public final ClassSelector deobfClasses; | 11 | public final ClassSelector deobfClasses; |
| 15 | private final Gui gui; | 12 | private final Gui gui; |
| 16 | 13 | ||
| 17 | public PanelDeobf(Gui gui) { | 14 | public PanelDeobf(Gui gui) { |
| 18 | this.gui = gui; | 15 | this.gui = gui; |
| 19 | 16 | ||
| 20 | this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); | 17 | this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); |
| 21 | this.deobfClasses.setSelectionListener(gui::navigateTo); | 18 | this.deobfClasses.setSelectionListener(gui::navigateTo); |
| 22 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); | 19 | this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 23 | 20 | ||
| 24 | this.setLayout(new BorderLayout()); | 21 | this.setLayout(new BorderLayout()); |
| 25 | this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); | 22 | this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); |
| 26 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); | 23 | this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); |
| 27 | } | 24 | } |
| 28 | } | 25 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java index 914952b9..fd30e389 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java | |||
| @@ -1,73 +1,71 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.gui.BrowserCaret; | ||
| 4 | import cuchaz.enigma.gui.Gui; | ||
| 5 | |||
| 6 | import javax.swing.*; | ||
| 3 | import java.awt.*; | 7 | import java.awt.*; |
| 4 | import java.awt.event.KeyAdapter; | 8 | import java.awt.event.KeyAdapter; |
| 5 | import java.awt.event.KeyEvent; | 9 | import java.awt.event.KeyEvent; |
| 6 | import java.awt.event.MouseAdapter; | 10 | import java.awt.event.MouseAdapter; |
| 7 | import java.awt.event.MouseEvent; | 11 | import java.awt.event.MouseEvent; |
| 8 | 12 | ||
| 9 | import javax.swing.JEditorPane; | ||
| 10 | |||
| 11 | import cuchaz.enigma.gui.BrowserCaret; | ||
| 12 | import cuchaz.enigma.gui.Gui; | ||
| 13 | |||
| 14 | public class PanelEditor extends JEditorPane { | 13 | public class PanelEditor extends JEditorPane { |
| 15 | private final Gui gui; | 14 | private final Gui gui; |
| 16 | 15 | ||
| 17 | public PanelEditor(Gui gui) { | 16 | public PanelEditor(Gui gui) { |
| 18 | this.gui = gui; | 17 | this.gui = gui; |
| 19 | 18 | ||
| 20 | this.setEditable(false); | 19 | this.setEditable(false); |
| 21 | this.setSelectionColor(new Color(31, 46, 90)); | 20 | this.setSelectionColor(new Color(31, 46, 90)); |
| 22 | this.setCaret(new BrowserCaret()); | 21 | this.setCaret(new BrowserCaret()); |
| 23 | this.addCaretListener(event -> gui.onCaretMove(event.getDot())); | 22 | this.addCaretListener(event -> gui.onCaretMove(event.getDot())); |
| 24 | final PanelEditor self = this; | 23 | final PanelEditor self = this; |
| 25 | this.addMouseListener(new MouseAdapter() | 24 | this.addMouseListener(new MouseAdapter() { |
| 26 | { | 25 | @Override |
| 27 | @Override public void mouseReleased(MouseEvent e) | 26 | public void mouseReleased(MouseEvent e) { |
| 28 | { | 27 | if (e.getButton() == MouseEvent.BUTTON3) |
| 29 | if (e.getButton() == MouseEvent.BUTTON3) | 28 | self.setCaretPosition(self.viewToModel(e.getPoint())); |
| 30 | self.setCaretPosition(self.viewToModel(e.getPoint())); | 29 | } |
| 31 | } | 30 | }); |
| 32 | }); | 31 | this.addKeyListener(new KeyAdapter() { |
| 33 | this.addKeyListener(new KeyAdapter() { | 32 | @Override |
| 34 | @Override | 33 | public void keyPressed(KeyEvent event) { |
| 35 | public void keyPressed(KeyEvent event) { | 34 | switch (event.getKeyCode()) { |
| 36 | switch (event.getKeyCode()) { | 35 | case KeyEvent.VK_R: |
| 37 | case KeyEvent.VK_R: | 36 | gui.popupMenu.renameMenu.doClick(); |
| 38 | gui.popupMenu.renameMenu.doClick(); | 37 | break; |
| 39 | break; | ||
| 40 | 38 | ||
| 41 | case KeyEvent.VK_I: | 39 | case KeyEvent.VK_I: |
| 42 | gui.popupMenu.showInheritanceMenu.doClick(); | 40 | gui.popupMenu.showInheritanceMenu.doClick(); |
| 43 | break; | 41 | break; |
| 44 | 42 | ||
| 45 | case KeyEvent.VK_M: | 43 | case KeyEvent.VK_M: |
| 46 | gui.popupMenu.showImplementationsMenu.doClick(); | 44 | gui.popupMenu.showImplementationsMenu.doClick(); |
| 47 | break; | 45 | break; |
| 48 | 46 | ||
| 49 | case KeyEvent.VK_N: | 47 | case KeyEvent.VK_N: |
| 50 | gui.popupMenu.openEntryMenu.doClick(); | 48 | gui.popupMenu.openEntryMenu.doClick(); |
| 51 | break; | 49 | break; |
| 52 | 50 | ||
| 53 | case KeyEvent.VK_P: | 51 | case KeyEvent.VK_P: |
| 54 | gui.popupMenu.openPreviousMenu.doClick(); | 52 | gui.popupMenu.openPreviousMenu.doClick(); |
| 55 | break; | 53 | break; |
| 56 | 54 | ||
| 57 | case KeyEvent.VK_C: | 55 | case KeyEvent.VK_C: |
| 58 | gui.popupMenu.showCallsMenu.doClick(); | 56 | gui.popupMenu.showCallsMenu.doClick(); |
| 59 | break; | 57 | break; |
| 60 | 58 | ||
| 61 | case KeyEvent.VK_T: | 59 | case KeyEvent.VK_T: |
| 62 | gui.popupMenu.toggleMappingMenu.doClick(); | 60 | gui.popupMenu.toggleMappingMenu.doClick(); |
| 63 | break; | 61 | break; |
| 64 | case KeyEvent.VK_F5: | 62 | case KeyEvent.VK_F5: |
| 65 | gui.getController().refreshCurrentClass(); | 63 | gui.getController().refreshCurrentClass(); |
| 66 | break; | 64 | break; |
| 67 | default: | 65 | default: |
| 68 | break; | 66 | break; |
| 69 | } | 67 | } |
| 70 | } | 68 | } |
| 71 | }); | 69 | }); |
| 72 | } | 70 | } |
| 73 | } | 71 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java index faa023bd..1bf68879 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java | |||
| @@ -1,34 +1,30 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.Dimension; | ||
| 4 | import java.awt.GridLayout; | ||
| 5 | |||
| 6 | import javax.swing.BorderFactory; | ||
| 7 | import javax.swing.JLabel; | ||
| 8 | import javax.swing.JPanel; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.Gui; | 3 | import cuchaz.enigma.gui.Gui; |
| 11 | import cuchaz.enigma.utils.Utils; | 4 | import cuchaz.enigma.utils.Utils; |
| 12 | 5 | ||
| 6 | import javax.swing.*; | ||
| 7 | import java.awt.*; | ||
| 8 | |||
| 13 | public class PanelIdentifier extends JPanel { | 9 | public class PanelIdentifier extends JPanel { |
| 14 | 10 | ||
| 15 | private final Gui gui; | 11 | private final Gui gui; |
| 16 | 12 | ||
| 17 | public PanelIdentifier(Gui gui) { | 13 | public PanelIdentifier(Gui gui) { |
| 18 | this.gui = gui; | 14 | this.gui = gui; |
| 19 | 15 | ||
| 20 | this.setLayout(new GridLayout(4, 1, 0, 0)); | 16 | this.setLayout(new GridLayout(4, 1, 0, 0)); |
| 21 | this.setPreferredSize(new Dimension(0, 100)); | 17 | this.setPreferredSize(new Dimension(0, 100)); |
| 22 | this.setBorder(BorderFactory.createTitledBorder("Identifier Info")); | 18 | this.setBorder(BorderFactory.createTitledBorder("Identifier Info")); |
| 23 | } | 19 | } |
| 24 | 20 | ||
| 25 | public void clearReference() { | 21 | public void clearReference() { |
| 26 | this.removeAll(); | 22 | this.removeAll(); |
| 27 | JLabel label = new JLabel("No identifier selected"); | 23 | JLabel label = new JLabel("No identifier selected"); |
| 28 | Utils.unboldLabel(label); | 24 | Utils.unboldLabel(label); |
| 29 | label.setHorizontalAlignment(JLabel.CENTER); | 25 | label.setHorizontalAlignment(JLabel.CENTER); |
| 30 | this.add(label); | 26 | this.add(label); |
| 31 | 27 | ||
| 32 | gui.redraw(); | 28 | gui.redraw(); |
| 33 | } | 29 | } |
| 34 | } | 30 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java index 27bb70b4..4bbd32bd 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java | |||
| @@ -1,39 +1,36 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | 1 | package cuchaz.enigma.gui.panels; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | ||
| 4 | import java.util.Comparator; | ||
| 5 | |||
| 6 | import javax.swing.JLabel; | ||
| 7 | import javax.swing.JPanel; | ||
| 8 | import javax.swing.JScrollPane; | ||
| 9 | |||
| 10 | import cuchaz.enigma.gui.ClassSelector; | 3 | import cuchaz.enigma.gui.ClassSelector; |
| 11 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 12 | import cuchaz.enigma.mapping.ClassEntry; | 5 | import cuchaz.enigma.mapping.ClassEntry; |
| 13 | 6 | ||
| 7 | import javax.swing.*; | ||
| 8 | import java.awt.*; | ||
| 9 | import java.util.Comparator; | ||
| 10 | |||
| 14 | public class PanelObf extends JPanel { | 11 | public class PanelObf extends JPanel { |
| 15 | 12 | ||
| 16 | private final Gui gui; | 13 | public final ClassSelector obfClasses; |
| 17 | public final ClassSelector obfClasses; | 14 | private final Gui gui; |
| 18 | 15 | ||
| 19 | public PanelObf(Gui gui) { | 16 | public PanelObf(Gui gui) { |
| 20 | this.gui = gui; | 17 | this.gui = gui; |
| 21 | 18 | ||
| 22 | Comparator<ClassEntry> obfClassComparator = (a, b) -> { | 19 | Comparator<ClassEntry> obfClassComparator = (a, b) -> { |
| 23 | String aname = a.getName(); | 20 | String aname = a.getName(); |
| 24 | String bname = b.getName(); | 21 | String bname = b.getName(); |
| 25 | if (aname.length() != bname.length()) { | 22 | if (aname.length() != bname.length()) { |
| 26 | return aname.length() - bname.length(); | 23 | return aname.length() - bname.length(); |
| 27 | } | 24 | } |
| 28 | return aname.compareTo(bname); | 25 | return aname.compareTo(bname); |
| 29 | }; | 26 | }; |
| 30 | 27 | ||
| 31 | this.obfClasses = new ClassSelector(gui, obfClassComparator, false); | 28 | this.obfClasses = new ClassSelector(gui, obfClassComparator, false); |
| 32 | this.obfClasses.setSelectionListener(gui::navigateTo); | 29 | this.obfClasses.setSelectionListener(gui::navigateTo); |
| 33 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); | 30 | this.obfClasses.setRenameSelectionListener(gui::onPanelRename); |
| 34 | 31 | ||
| 35 | this.setLayout(new BorderLayout()); | 32 | this.setLayout(new BorderLayout()); |
| 36 | this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); | 33 | this.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH); |
| 37 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); | 34 | this.add(new JScrollPane(this.obfClasses), BorderLayout.CENTER); |
| 38 | } | 35 | } |
| 39 | } | 36 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java index 662516da..9154cc22 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java | |||
| @@ -8,102 +8,103 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.utils.Utils; | 14 | import cuchaz.enigma.utils.Utils; |
| 14 | 15 | ||
| 15 | public class ArgumentEntry implements Entry { | 16 | public class ArgumentEntry implements Entry { |
| 16 | 17 | ||
| 17 | private BehaviorEntry behaviorEntry; | 18 | private BehaviorEntry behaviorEntry; |
| 18 | private int index; | 19 | private int index; |
| 19 | private String name; | 20 | private String name; |
| 20 | 21 | ||
| 21 | public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { | 22 | public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { |
| 22 | if (behaviorEntry == null) { | 23 | if (behaviorEntry == null) { |
| 23 | throw new IllegalArgumentException("Behavior cannot be null!"); | 24 | throw new IllegalArgumentException("Behavior cannot be null!"); |
| 24 | } | 25 | } |
| 25 | if (index < 0) { | 26 | if (index < 0) { |
| 26 | throw new IllegalArgumentException("Index must be non-negative!"); | 27 | throw new IllegalArgumentException("Index must be non-negative!"); |
| 27 | } | 28 | } |
| 28 | if (name == null) { | 29 | if (name == null) { |
| 29 | throw new IllegalArgumentException("Argument name cannot be null!"); | 30 | throw new IllegalArgumentException("Argument name cannot be null!"); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | this.behaviorEntry = behaviorEntry; | 33 | this.behaviorEntry = behaviorEntry; |
| 33 | this.index = index; | 34 | this.index = index; |
| 34 | this.name = name; | 35 | this.name = name; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | public ArgumentEntry(ArgumentEntry other) { | 38 | public ArgumentEntry(ArgumentEntry other) { |
| 38 | this.behaviorEntry = other.getBehaviorEntry(); | 39 | this.behaviorEntry = other.getBehaviorEntry(); |
| 39 | this.index = other.index; | 40 | this.index = other.index; |
| 40 | this.name = other.name; | 41 | this.name = other.name; |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | public ArgumentEntry(ArgumentEntry other, String newClassName) { | 44 | public ArgumentEntry(ArgumentEntry other, String newClassName) { |
| 44 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); | 45 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); |
| 45 | this.index = other.index; | 46 | this.index = other.index; |
| 46 | this.name = other.name; | 47 | this.name = other.name; |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { | 50 | public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { |
| 50 | this.behaviorEntry = entry; | 51 | this.behaviorEntry = entry; |
| 51 | this.index = other.index; | 52 | this.index = other.index; |
| 52 | this.name = other.name; | 53 | this.name = other.name; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | public BehaviorEntry getBehaviorEntry() { | 56 | public BehaviorEntry getBehaviorEntry() { |
| 56 | return this.behaviorEntry; | 57 | return this.behaviorEntry; |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | public int getIndex() { | 60 | public int getIndex() { |
| 60 | return this.index; | 61 | return this.index; |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | @Override | 64 | @Override |
| 64 | public String getName() { | 65 | public String getName() { |
| 65 | return this.name; | 66 | return this.name; |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | @Override | 69 | @Override |
| 69 | public ClassEntry getClassEntry() { | 70 | public ClassEntry getClassEntry() { |
| 70 | return this.behaviorEntry.getClassEntry(); | 71 | return this.behaviorEntry.getClassEntry(); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | @Override | 74 | @Override |
| 74 | public String getClassName() { | 75 | public String getClassName() { |
| 75 | return this.behaviorEntry.getClassName(); | 76 | return this.behaviorEntry.getClassName(); |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | @Override | 79 | @Override |
| 79 | public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { | 80 | public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { |
| 80 | return new ArgumentEntry(this, classEntry.getName()); | 81 | return new ArgumentEntry(this, classEntry.getName()); |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | public String getMethodName() { | 84 | public String getMethodName() { |
| 84 | return this.behaviorEntry.getName(); | 85 | return this.behaviorEntry.getName(); |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 87 | public Signature getMethodSignature() { | 88 | public Signature getMethodSignature() { |
| 88 | return this.behaviorEntry.getSignature(); | 89 | return this.behaviorEntry.getSignature(); |
| 89 | } | 90 | } |
| 90 | 91 | ||
| 91 | @Override | 92 | @Override |
| 92 | public int hashCode() { | 93 | public int hashCode() { |
| 93 | return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); | 94 | return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); |
| 94 | } | 95 | } |
| 95 | 96 | ||
| 96 | @Override | 97 | @Override |
| 97 | public boolean equals(Object other) { | 98 | public boolean equals(Object other) { |
| 98 | return other instanceof ArgumentEntry && equals((ArgumentEntry) other); | 99 | return other instanceof ArgumentEntry && equals((ArgumentEntry) other); |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | public boolean equals(ArgumentEntry other) { | 102 | public boolean equals(ArgumentEntry other) { |
| 102 | return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); | 103 | return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | @Override | 106 | @Override |
| 106 | public String toString() { | 107 | public String toString() { |
| 107 | return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ")"; | 108 | return this.behaviorEntry + "(" + this.index + ":" + this.name + ")"; |
| 108 | } | 109 | } |
| 109 | } | 110 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java index e3f89272..91ecd106 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java | |||
| @@ -8,42 +8,43 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | public class ArgumentMapping implements Comparable<ArgumentMapping> { | 14 | public class ArgumentMapping implements Comparable<ArgumentMapping> { |
| 14 | 15 | ||
| 15 | private int index; | 16 | private int index; |
| 16 | private String name; | 17 | private String name; |
| 17 | 18 | ||
| 18 | // NOTE: this argument order is important for the MethodReader/MethodWriter | 19 | // NOTE: this argument order is important for the MethodReader/MethodWriter |
| 19 | public ArgumentMapping(int index, String name) { | 20 | public ArgumentMapping(int index, String name) { |
| 20 | this.index = index; | 21 | this.index = index; |
| 21 | this.name = NameValidator.validateArgumentName(name); | 22 | this.name = NameValidator.validateArgumentName(name); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | public ArgumentMapping(ArgumentMapping other) { | 25 | public ArgumentMapping(ArgumentMapping other) { |
| 25 | this.index = other.index; | 26 | this.index = other.index; |
| 26 | this.name = other.name; | 27 | this.name = other.name; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | public int getIndex() { | 30 | public int getIndex() { |
| 30 | return this.index; | 31 | return this.index; |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | public String getName() { | 34 | public String getName() { |
| 34 | return this.name; | 35 | return this.name; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | public void setName(String val) { | 38 | public void setName(String val) { |
| 38 | this.name = NameValidator.validateArgumentName(val); | 39 | this.name = NameValidator.validateArgumentName(val); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { | 42 | public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { |
| 42 | return new ArgumentEntry(behaviorEntry, index, name); | 43 | return new ArgumentEntry(behaviorEntry, index, name); |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | @Override | 46 | @Override |
| 46 | public int compareTo(ArgumentMapping other) { | 47 | public int compareTo(ArgumentMapping other) { |
| 47 | return Integer.compare(this.index, other.index); | 48 | return Integer.compare(this.index, other.index); |
| 48 | } | 49 | } |
| 49 | } | 50 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java index f5c6c051..04b4ebc7 100644 --- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java | |||
| @@ -8,8 +8,9 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | public interface BehaviorEntry extends Entry { | 14 | public interface BehaviorEntry extends Entry { |
| 14 | Signature getSignature(); | 15 | Signature getSignature(); |
| 15 | } | 16 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java index 398b1355..788811ff 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -16,151 +17,151 @@ import java.util.List; | |||
| 16 | 17 | ||
| 17 | public class ClassEntry implements Entry { | 18 | public class ClassEntry implements Entry { |
| 18 | 19 | ||
| 19 | private String name; | 20 | private String name; |
| 20 | 21 | ||
| 21 | public ClassEntry(String className) { | 22 | public ClassEntry(String className) { |
| 22 | if (className == null) { | 23 | if (className == null) { |
| 23 | throw new IllegalArgumentException("Class name cannot be null!"); | 24 | throw new IllegalArgumentException("Class name cannot be null!"); |
| 24 | } | 25 | } |
| 25 | if (className.indexOf('.') >= 0) { | 26 | if (className.indexOf('.') >= 0) { |
| 26 | throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); | 27 | throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | this.name = className; | 30 | this.name = className; |
| 30 | 31 | ||
| 31 | if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { | 32 | if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { |
| 32 | throw new IllegalArgumentException("Inner class must not have a package: " + className); | 33 | throw new IllegalArgumentException("Inner class must not have a package: " + className); |
| 33 | } | 34 | } |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | public ClassEntry(ClassEntry other) { | 37 | public ClassEntry(ClassEntry other) { |
| 37 | this.name = other.name; | 38 | this.name = other.name; |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | @Override | 41 | @Override |
| 41 | public String getName() { | 42 | public String getName() { |
| 42 | return this.name; | 43 | return this.name; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | @Override | 46 | @Override |
| 46 | public String getClassName() { | 47 | public String getClassName() { |
| 47 | return this.name; | 48 | return this.name; |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | @Override | 51 | @Override |
| 51 | public ClassEntry getClassEntry() { | 52 | public ClassEntry getClassEntry() { |
| 52 | return this; | 53 | return this; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | @Override | 56 | @Override |
| 56 | public ClassEntry cloneToNewClass(ClassEntry classEntry) { | 57 | public ClassEntry cloneToNewClass(ClassEntry classEntry) { |
| 57 | return classEntry; | 58 | return classEntry; |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | @Override | 61 | @Override |
| 61 | public int hashCode() { | 62 | public int hashCode() { |
| 62 | return this.name.hashCode(); | 63 | return this.name.hashCode(); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | @Override | 66 | @Override |
| 66 | public boolean equals(Object other) { | 67 | public boolean equals(Object other) { |
| 67 | return other instanceof ClassEntry && equals((ClassEntry) other); | 68 | return other instanceof ClassEntry && equals((ClassEntry) other); |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | public boolean equals(ClassEntry other) { | 71 | public boolean equals(ClassEntry other) { |
| 71 | return other != null && this.name.equals(other.name); | 72 | return other != null && this.name.equals(other.name); |
| 72 | } | 73 | } |
| 73 | 74 | ||
| 74 | @Override | 75 | @Override |
| 75 | public String toString() { | 76 | public String toString() { |
| 76 | return this.name; | 77 | return this.name; |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | public boolean isInnerClass() { | 80 | public boolean isInnerClass() { |
| 80 | return this.name.lastIndexOf('$') >= 0; | 81 | return this.name.lastIndexOf('$') >= 0; |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | public List<String> getClassChainNames() { | 84 | public List<String> getClassChainNames() { |
| 84 | return Lists.newArrayList(this.name.split("\\$")); | 85 | return Lists.newArrayList(this.name.split("\\$")); |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 87 | public List<ClassEntry> getClassChain() { | 88 | public List<ClassEntry> getClassChain() { |
| 88 | List<ClassEntry> entries = Lists.newArrayList(); | 89 | List<ClassEntry> entries = Lists.newArrayList(); |
| 89 | StringBuilder buf = new StringBuilder(); | 90 | StringBuilder buf = new StringBuilder(); |
| 90 | for (String name : getClassChainNames()) { | 91 | for (String name : getClassChainNames()) { |
| 91 | if (buf.length() > 0) { | 92 | if (buf.length() > 0) { |
| 92 | buf.append("$"); | 93 | buf.append("$"); |
| 93 | } | 94 | } |
| 94 | buf.append(name); | 95 | buf.append(name); |
| 95 | entries.add(new ClassEntry(buf.toString())); | 96 | entries.add(new ClassEntry(buf.toString())); |
| 96 | } | 97 | } |
| 97 | return entries; | 98 | return entries; |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | public String getOutermostClassName() { | 101 | public String getOutermostClassName() { |
| 101 | if (isInnerClass()) { | 102 | if (isInnerClass()) { |
| 102 | return this.name.substring(0, this.name.indexOf('$')); | 103 | return this.name.substring(0, this.name.indexOf('$')); |
| 103 | } | 104 | } |
| 104 | return this.name; | 105 | return this.name; |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | public ClassEntry getOutermostClassEntry() { | 108 | public ClassEntry getOutermostClassEntry() { |
| 108 | return new ClassEntry(getOutermostClassName()); | 109 | return new ClassEntry(getOutermostClassName()); |
| 109 | } | 110 | } |
| 110 | 111 | ||
| 111 | public String getOuterClassName() { | 112 | public String getOuterClassName() { |
| 112 | if (!isInnerClass()) { | 113 | if (!isInnerClass()) { |
| 113 | throw new Error("This is not an inner class!"); | 114 | throw new Error("This is not an inner class!"); |
| 114 | } | 115 | } |
| 115 | return this.name.substring(0, this.name.lastIndexOf('$')); | 116 | return this.name.substring(0, this.name.lastIndexOf('$')); |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | public ClassEntry getOuterClassEntry() { | 119 | public ClassEntry getOuterClassEntry() { |
| 119 | return new ClassEntry(getOuterClassName()); | 120 | return new ClassEntry(getOuterClassName()); |
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | public String getInnermostClassName() { | 123 | public String getInnermostClassName() { |
| 123 | if (!isInnerClass()) { | 124 | if (!isInnerClass()) { |
| 124 | throw new Error("This is not an inner class!"); | 125 | throw new Error("This is not an inner class!"); |
| 125 | } | 126 | } |
| 126 | return this.name.substring(this.name.lastIndexOf('$') + 1); | 127 | return this.name.substring(this.name.lastIndexOf('$') + 1); |
| 127 | } | 128 | } |
| 128 | 129 | ||
| 129 | public boolean isInDefaultPackage() { | 130 | public boolean isInDefaultPackage() { |
| 130 | return this.name.indexOf('/') < 0; | 131 | return this.name.indexOf('/') < 0; |
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | public String getPackageName() { | 134 | public String getPackageName() { |
| 134 | int pos = this.name.lastIndexOf('/'); | 135 | int pos = this.name.lastIndexOf('/'); |
| 135 | if (pos > 0) { | 136 | if (pos > 0) { |
| 136 | return this.name.substring(0, pos); | 137 | return this.name.substring(0, pos); |
| 137 | } | 138 | } |
| 138 | return null; | 139 | return null; |
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | public String getSimpleName() { | 142 | public String getSimpleName() { |
| 142 | int pos = this.name.lastIndexOf('/'); | 143 | int pos = this.name.lastIndexOf('/'); |
| 143 | if (pos > 0) { | 144 | if (pos > 0) { |
| 144 | return this.name.substring(pos + 1); | 145 | return this.name.substring(pos + 1); |
| 145 | } | 146 | } |
| 146 | return this.name; | 147 | return this.name; |
| 147 | } | 148 | } |
| 148 | 149 | ||
| 149 | public ClassEntry buildClassEntry(List<ClassEntry> classChain) { | 150 | public ClassEntry buildClassEntry(List<ClassEntry> classChain) { |
| 150 | assert (classChain.contains(this)); | 151 | assert (classChain.contains(this)); |
| 151 | StringBuilder buf = new StringBuilder(); | 152 | StringBuilder buf = new StringBuilder(); |
| 152 | for (ClassEntry chainEntry : classChain) { | 153 | for (ClassEntry chainEntry : classChain) { |
| 153 | if (buf.length() == 0) { | 154 | if (buf.length() == 0) { |
| 154 | buf.append(chainEntry.getName()); | 155 | buf.append(chainEntry.getName()); |
| 155 | } else { | 156 | } else { |
| 156 | buf.append("$"); | 157 | buf.append("$"); |
| 157 | buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); | 158 | buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); |
| 158 | } | 159 | } |
| 159 | 160 | ||
| 160 | if (chainEntry == this) { | 161 | if (chainEntry == this) { |
| 161 | break; | 162 | break; |
| 162 | } | 163 | } |
| 163 | } | 164 | } |
| 164 | return new ClassEntry(buf.toString()); | 165 | return new ClassEntry(buf.toString()); |
| 165 | } | 166 | } |
| 166 | } | 167 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index a261c912..178dd3c6 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java | |||
| @@ -8,540 +8,530 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 15 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 14 | 16 | ||
| 15 | import java.util.ArrayList; | 17 | import java.util.ArrayList; |
| 16 | import java.util.Map; | 18 | import java.util.Map; |
| 17 | 19 | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 19 | |||
| 20 | // FIXME: Enigma doesn't support inner classes of inner class????! | 20 | // FIXME: Enigma doesn't support inner classes of inner class????! |
| 21 | public class ClassMapping implements Comparable<ClassMapping> { | 21 | public class ClassMapping implements Comparable<ClassMapping> { |
| 22 | 22 | ||
| 23 | private String obfFullName; | 23 | private String obfFullName; |
| 24 | private String obfSimpleName; | 24 | private String obfSimpleName; |
| 25 | private String deobfName; | 25 | private String deobfName; |
| 26 | private String previousDeobfName; | 26 | private String previousDeobfName; |
| 27 | private Map<String, ClassMapping> innerClassesByObfSimple; | 27 | private Map<String, ClassMapping> innerClassesByObfSimple; |
| 28 | private Map<String, ClassMapping> innerClassesByObfFull; | 28 | private Map<String, ClassMapping> innerClassesByObfFull; |
| 29 | private Map<String, ClassMapping> innerClassesByDeobf; | 29 | private Map<String, ClassMapping> innerClassesByDeobf; |
| 30 | private Map<String, FieldMapping> fieldsByObf; | 30 | private Map<String, FieldMapping> fieldsByObf; |
| 31 | private Map<String, FieldMapping> fieldsByDeobf; | 31 | private Map<String, FieldMapping> fieldsByDeobf; |
| 32 | private Map<String, MethodMapping> methodsByObf; | 32 | private Map<String, MethodMapping> methodsByObf; |
| 33 | private Map<String, MethodMapping> methodsByDeobf; | 33 | private Map<String, MethodMapping> methodsByDeobf; |
| 34 | private boolean isDirty; | 34 | private boolean isDirty; |
| 35 | private Mappings.EntryModifier modifier; | 35 | private Mappings.EntryModifier modifier; |
| 36 | 36 | ||
| 37 | public ClassMapping(String obfFullName) | 37 | public ClassMapping(String obfFullName) { |
| 38 | { | 38 | this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); |
| 39 | this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); | 39 | } |
| 40 | } | 40 | |
| 41 | 41 | public ClassMapping(String obfFullName, String deobfName) { | |
| 42 | public ClassMapping(String obfFullName, String deobfName) | 42 | this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); |
| 43 | { | 43 | } |
| 44 | this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); | 44 | |
| 45 | } | 45 | public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) { |
| 46 | 46 | this.obfFullName = obfFullName; | |
| 47 | public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) | 47 | ClassEntry classEntry = new ClassEntry(obfFullName); |
| 48 | { | 48 | obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); |
| 49 | this.obfFullName = obfFullName; | 49 | previousDeobfName = null; |
| 50 | ClassEntry classEntry = new ClassEntry(obfFullName); | 50 | this.deobfName = NameValidator.validateClassName(deobfName, false); |
| 51 | obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); | 51 | innerClassesByObfSimple = Maps.newHashMap(); |
| 52 | previousDeobfName = null; | 52 | innerClassesByObfFull = Maps.newHashMap(); |
| 53 | this.deobfName = NameValidator.validateClassName(deobfName, false); | 53 | innerClassesByDeobf = Maps.newHashMap(); |
| 54 | innerClassesByObfSimple = Maps.newHashMap(); | 54 | fieldsByObf = Maps.newHashMap(); |
| 55 | innerClassesByObfFull = Maps.newHashMap(); | 55 | fieldsByDeobf = Maps.newHashMap(); |
| 56 | innerClassesByDeobf = Maps.newHashMap(); | 56 | methodsByObf = Maps.newHashMap(); |
| 57 | fieldsByObf = Maps.newHashMap(); | 57 | methodsByDeobf = Maps.newHashMap(); |
| 58 | fieldsByDeobf = Maps.newHashMap(); | 58 | isDirty = true; |
| 59 | methodsByObf = Maps.newHashMap(); | 59 | this.modifier = modifier; |
| 60 | methodsByDeobf = Maps.newHashMap(); | 60 | } |
| 61 | isDirty = true; | 61 | |
| 62 | this.modifier = modifier; | 62 | public static boolean isSimpleClassName(String name) { |
| 63 | } | 63 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; |
| 64 | 64 | } | |
| 65 | public String getObfFullName() { | 65 | |
| 66 | return obfFullName; | 66 | public String getObfFullName() { |
| 67 | } | 67 | return obfFullName; |
| 68 | 68 | } | |
| 69 | public String getObfSimpleName() { | 69 | |
| 70 | return obfSimpleName; | 70 | public String getObfSimpleName() { |
| 71 | } | 71 | return obfSimpleName; |
| 72 | 72 | } | |
| 73 | public String getPreviousDeobfName() { | 73 | |
| 74 | return previousDeobfName; | 74 | public String getPreviousDeobfName() { |
| 75 | } | 75 | return previousDeobfName; |
| 76 | 76 | } | |
| 77 | public String getDeobfName() { | 77 | |
| 78 | return deobfName; | 78 | public String getDeobfName() { |
| 79 | } | 79 | return deobfName; |
| 80 | 80 | } | |
| 81 | public void setDeobfName(String val) { | 81 | |
| 82 | previousDeobfName = deobfName; | 82 | //// INNER CLASSES //////// |
| 83 | deobfName = NameValidator.validateClassName(val, false); | 83 | |
| 84 | this.isDirty = true; | 84 | public void setDeobfName(String val) { |
| 85 | } | 85 | previousDeobfName = deobfName; |
| 86 | 86 | deobfName = NameValidator.validateClassName(val, false); | |
| 87 | //// INNER CLASSES //////// | 87 | this.isDirty = true; |
| 88 | 88 | } | |
| 89 | public Iterable<ClassMapping> innerClasses() { | 89 | |
| 90 | assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); | 90 | public Iterable<ClassMapping> innerClasses() { |
| 91 | return innerClassesByObfSimple.values(); | 91 | assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); |
| 92 | } | 92 | return innerClassesByObfSimple.values(); |
| 93 | 93 | } | |
| 94 | public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { | 94 | |
| 95 | // FIXME: dirty hack, that can get into issues, but it's a temp fix! | 95 | public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { |
| 96 | if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { | 96 | // FIXME: dirty hack, that can get into issues, but it's a temp fix! |
| 97 | throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); | 97 | if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { |
| 98 | } | 98 | throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); |
| 99 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); | 99 | } |
| 100 | innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); | 100 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); |
| 101 | 101 | innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); | |
| 102 | if (classMapping.getDeobfName() != null) { | 102 | |
| 103 | if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { | 103 | if (classMapping.getDeobfName() != null) { |
| 104 | throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); | 104 | if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { |
| 105 | } | 105 | throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); |
| 106 | innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); | 106 | } |
| 107 | } | 107 | innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); |
| 108 | this.isDirty = true; | 108 | } |
| 109 | } | 109 | this.isDirty = true; |
| 110 | 110 | } | |
| 111 | public void removeInnerClassMapping(ClassMapping classMapping) { | 111 | |
| 112 | innerClassesByObfFull.remove(classMapping.getObfFullName()); | 112 | public void removeInnerClassMapping(ClassMapping classMapping) { |
| 113 | boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; | 113 | innerClassesByObfFull.remove(classMapping.getObfFullName()); |
| 114 | assert (obfWasRemoved); | 114 | boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; |
| 115 | if (classMapping.getDeobfName() != null) { | 115 | assert (obfWasRemoved); |
| 116 | boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | 116 | if (classMapping.getDeobfName() != null) { |
| 117 | assert (deobfWasRemoved); | 117 | boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; |
| 118 | } | 118 | assert (deobfWasRemoved); |
| 119 | this.isDirty = true; | 119 | } |
| 120 | } | 120 | this.isDirty = true; |
| 121 | 121 | } | |
| 122 | public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { | 122 | |
| 123 | ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); | 123 | public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { |
| 124 | if (classMapping == null) { | 124 | ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); |
| 125 | classMapping = new ClassMapping(obfInnerClass.getName()); | 125 | if (classMapping == null) { |
| 126 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); | 126 | classMapping = new ClassMapping(obfInnerClass.getName()); |
| 127 | boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; | 127 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); |
| 128 | assert (wasAdded); | 128 | boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; |
| 129 | this.isDirty = true; | 129 | assert (wasAdded); |
| 130 | } | 130 | this.isDirty = true; |
| 131 | return classMapping; | 131 | } |
| 132 | } | 132 | return classMapping; |
| 133 | 133 | } | |
| 134 | public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { | 134 | |
| 135 | assert (isSimpleClassName(obfSimpleName)); | 135 | public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { |
| 136 | return innerClassesByObfSimple.get(obfSimpleName); | 136 | assert (isSimpleClassName(obfSimpleName)); |
| 137 | } | 137 | return innerClassesByObfSimple.get(obfSimpleName); |
| 138 | 138 | } | |
| 139 | public ClassMapping getInnerClassByDeobf(String deobfName) { | 139 | |
| 140 | assert (isSimpleClassName(deobfName)); | 140 | public ClassMapping getInnerClassByDeobf(String deobfName) { |
| 141 | return innerClassesByDeobf.get(deobfName); | 141 | assert (isSimpleClassName(deobfName)); |
| 142 | } | 142 | return innerClassesByDeobf.get(deobfName); |
| 143 | 143 | } | |
| 144 | public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { | 144 | |
| 145 | ClassMapping classMapping = getInnerClassByDeobf(name); | 145 | public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { |
| 146 | if (classMapping == null) { | 146 | ClassMapping classMapping = getInnerClassByDeobf(name); |
| 147 | classMapping = getInnerClassByObfSimple(name); | 147 | if (classMapping == null) { |
| 148 | } | 148 | classMapping = getInnerClassByObfSimple(name); |
| 149 | return classMapping; | 149 | } |
| 150 | } | 150 | return classMapping; |
| 151 | 151 | } | |
| 152 | public String getDeobfInnerClassName(String obfSimpleName) { | 152 | |
| 153 | assert (isSimpleClassName(obfSimpleName)); | 153 | public String getDeobfInnerClassName(String obfSimpleName) { |
| 154 | ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); | 154 | assert (isSimpleClassName(obfSimpleName)); |
| 155 | if (classMapping != null) { | 155 | ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); |
| 156 | return classMapping.getDeobfName(); | 156 | if (classMapping != null) { |
| 157 | } | 157 | return classMapping.getDeobfName(); |
| 158 | return null; | 158 | } |
| 159 | } | 159 | return null; |
| 160 | 160 | } | |
| 161 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { | 161 | |
| 162 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); | 162 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { |
| 163 | if (classMapping.getDeobfName() != null) { | 163 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); |
| 164 | boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | 164 | if (classMapping.getDeobfName() != null) { |
| 165 | assert (wasRemoved); | 165 | boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; |
| 166 | } | 166 | assert (wasRemoved); |
| 167 | classMapping.setDeobfName(deobfName); | 167 | } |
| 168 | if (deobfName != null) { | 168 | classMapping.setDeobfName(deobfName); |
| 169 | assert (isSimpleClassName(deobfName)); | 169 | if (deobfName != null) { |
| 170 | boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; | 170 | assert (isSimpleClassName(deobfName)); |
| 171 | assert (wasAdded); | 171 | boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; |
| 172 | } | 172 | assert (wasAdded); |
| 173 | this.isDirty = true; | 173 | } |
| 174 | } | 174 | this.isDirty = true; |
| 175 | 175 | } | |
| 176 | public boolean hasInnerClassByObfSimple(String obfSimpleName) { | 176 | |
| 177 | return innerClassesByObfSimple.containsKey(obfSimpleName); | 177 | public boolean hasInnerClassByObfSimple(String obfSimpleName) { |
| 178 | } | 178 | return innerClassesByObfSimple.containsKey(obfSimpleName); |
| 179 | 179 | } | |
| 180 | public boolean hasInnerClassByDeobf(String deobfName) { | 180 | |
| 181 | return innerClassesByDeobf.containsKey(deobfName); | 181 | //// FIELDS //////// |
| 182 | } | 182 | |
| 183 | 183 | public boolean hasInnerClassByDeobf(String deobfName) { | |
| 184 | 184 | return innerClassesByDeobf.containsKey(deobfName); | |
| 185 | //// FIELDS //////// | 185 | } |
| 186 | 186 | ||
| 187 | public Iterable<FieldMapping> fields() { | 187 | public Iterable<FieldMapping> fields() { |
| 188 | assert (fieldsByObf.size() == fieldsByDeobf.size()); | 188 | assert (fieldsByObf.size() == fieldsByDeobf.size()); |
| 189 | return fieldsByObf.values(); | 189 | return fieldsByObf.values(); |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | public boolean containsObfField(String obfName, Type obfType) { | 192 | public boolean containsObfField(String obfName, Type obfType) { |
| 193 | return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); | 193 | return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | public boolean containsDeobfField(String deobfName, Type deobfType) { | 196 | public boolean containsDeobfField(String deobfName, Type deobfType) { |
| 197 | return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); | 197 | return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | public void addFieldMapping(FieldMapping fieldMapping) { | 200 | public void addFieldMapping(FieldMapping fieldMapping) { |
| 201 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | 201 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); |
| 202 | if (fieldsByObf.containsKey(obfKey)) { | 202 | if (fieldsByObf.containsKey(obfKey)) { |
| 203 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); | 203 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); |
| 204 | } | 204 | } |
| 205 | if (fieldMapping.getDeobfName() != null) { | 205 | if (fieldMapping.getDeobfName() != null) { |
| 206 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); | 206 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); |
| 207 | if (fieldsByDeobf.containsKey(deobfKey)) { | 207 | if (fieldsByDeobf.containsKey(deobfKey)) { |
| 208 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); | 208 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); |
| 209 | } | 209 | } |
| 210 | boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; | 210 | boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; |
| 211 | assert (deobfWasAdded); | 211 | assert (deobfWasAdded); |
| 212 | } | 212 | } |
| 213 | boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; | 213 | boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; |
| 214 | assert (obfWasAdded); | 214 | assert (obfWasAdded); |
| 215 | this.isDirty = true; | 215 | this.isDirty = true; |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | public void removeFieldMapping(FieldMapping fieldMapping) { | 218 | public void removeFieldMapping(FieldMapping fieldMapping) { |
| 219 | boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; | 219 | boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; |
| 220 | assert (obfWasRemoved); | 220 | assert (obfWasRemoved); |
| 221 | if (fieldMapping.getDeobfName() != null) { | 221 | if (fieldMapping.getDeobfName() != null) { |
| 222 | boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; | 222 | boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; |
| 223 | assert (deobfWasRemoved); | 223 | assert (deobfWasRemoved); |
| 224 | } | 224 | } |
| 225 | this.isDirty = true; | 225 | this.isDirty = true; |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | public FieldMapping getFieldByObf(String obfName, Type obfType) { | 228 | public FieldMapping getFieldByObf(String obfName, Type obfType) { |
| 229 | return fieldsByObf.get(getFieldKey(obfName, obfType)); | 229 | return fieldsByObf.get(getFieldKey(obfName, obfType)); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { | 232 | public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { |
| 233 | return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | 233 | return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | public String getObfFieldName(String deobfName, Type obfType) { | 236 | public String getObfFieldName(String deobfName, Type obfType) { |
| 237 | FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | 237 | FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); |
| 238 | if (fieldMapping != null) { | 238 | if (fieldMapping != null) { |
| 239 | return fieldMapping.getObfName(); | 239 | return fieldMapping.getObfName(); |
| 240 | } | 240 | } |
| 241 | return null; | 241 | return null; |
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | public String getDeobfFieldName(String obfName, Type obfType) { | 244 | public String getDeobfFieldName(String obfName, Type obfType) { |
| 245 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); | 245 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); |
| 246 | if (fieldMapping != null) { | 246 | if (fieldMapping != null) { |
| 247 | return fieldMapping.getDeobfName(); | 247 | return fieldMapping.getDeobfName(); |
| 248 | } | 248 | } |
| 249 | return null; | 249 | return null; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | private String getFieldKey(String name, Type type) { | 252 | private String getFieldKey(String name, Type type) { |
| 253 | if (name == null) { | 253 | if (name == null) { |
| 254 | throw new IllegalArgumentException("name cannot be null!"); | 254 | throw new IllegalArgumentException("name cannot be null!"); |
| 255 | } | 255 | } |
| 256 | if (type == null) { | 256 | if (type == null) { |
| 257 | throw new IllegalArgumentException("type cannot be null!"); | 257 | throw new IllegalArgumentException("type cannot be null!"); |
| 258 | } | 258 | } |
| 259 | return name + ":" + type; | 259 | return name + ":" + type; |
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | public void setFieldName(String obfName, Type obfType, String deobfName) { | 262 | public void setFieldName(String obfName, Type obfType, String deobfName) { |
| 263 | assert (deobfName != null); | 263 | assert (deobfName != null); |
| 264 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); | 264 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); |
| 265 | if (fieldMapping == null) { | 265 | if (fieldMapping == null) { |
| 266 | fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); | 266 | fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); |
| 267 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; | 267 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; |
| 268 | assert (obfWasAdded); | 268 | assert (obfWasAdded); |
| 269 | } else { | 269 | } else { |
| 270 | boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; | 270 | boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; |
| 271 | assert (wasRemoved); | 271 | assert (wasRemoved); |
| 272 | } | 272 | } |
| 273 | fieldMapping.setDeobfName(deobfName); | 273 | fieldMapping.setDeobfName(deobfName); |
| 274 | if (deobfName != null) { | 274 | if (deobfName != null) { |
| 275 | boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; | 275 | boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; |
| 276 | assert (wasAdded); | 276 | assert (wasAdded); |
| 277 | } | 277 | } |
| 278 | this.isDirty = true; | 278 | this.isDirty = true; |
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { | 281 | //// METHODS //////// |
| 282 | assert(newObfName != null); | 282 | |
| 283 | FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); | 283 | public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { |
| 284 | assert(fieldMapping != null); | 284 | assert (newObfName != null); |
| 285 | fieldMapping.setObfName(newObfName); | 285 | FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); |
| 286 | fieldMapping.setObfType(newObfType); | 286 | assert (fieldMapping != null); |
| 287 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; | 287 | fieldMapping.setObfName(newObfName); |
| 288 | assert(obfWasAdded); | 288 | fieldMapping.setObfType(newObfType); |
| 289 | this.isDirty = true; | 289 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; |
| 290 | } | 290 | assert (obfWasAdded); |
| 291 | 291 | this.isDirty = true; | |
| 292 | //// METHODS //////// | 292 | } |
| 293 | 293 | ||
| 294 | public Iterable<MethodMapping> methods() { | 294 | public Iterable<MethodMapping> methods() { |
| 295 | assert (methodsByObf.size() >= methodsByDeobf.size()); | 295 | assert (methodsByObf.size() >= methodsByDeobf.size()); |
| 296 | return methodsByObf.values(); | 296 | return methodsByObf.values(); |
| 297 | } | 297 | } |
| 298 | 298 | ||
| 299 | public boolean containsObfMethod(String obfName, Signature obfSignature) { | 299 | public boolean containsObfMethod(String obfName, Signature obfSignature) { |
| 300 | return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); | 300 | return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); |
| 301 | } | 301 | } |
| 302 | 302 | ||
| 303 | public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { | 303 | public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { |
| 304 | return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); | 304 | return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | public void addMethodMapping(MethodMapping methodMapping) { | 307 | public void addMethodMapping(MethodMapping methodMapping) { |
| 308 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | 308 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); |
| 309 | if (methodsByObf.containsKey(obfKey)) { | 309 | if (methodsByObf.containsKey(obfKey)) { |
| 310 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); | 310 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); |
| 311 | } | 311 | } |
| 312 | boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; | 312 | boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; |
| 313 | assert (wasAdded); | 313 | assert (wasAdded); |
| 314 | if (methodMapping.getDeobfName() != null) { | 314 | if (methodMapping.getDeobfName() != null) { |
| 315 | String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); | 315 | String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); |
| 316 | if (methodsByDeobf.containsKey(deobfKey)) { | 316 | if (methodsByDeobf.containsKey(deobfKey)) { |
| 317 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); | 317 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); |
| 318 | } | 318 | } |
| 319 | boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; | 319 | boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; |
| 320 | assert (deobfWasAdded); | 320 | assert (deobfWasAdded); |
| 321 | } | 321 | } |
| 322 | this.isDirty = true; | 322 | this.isDirty = true; |
| 323 | assert (methodsByObf.size() >= methodsByDeobf.size()); | 323 | assert (methodsByObf.size() >= methodsByDeobf.size()); |
| 324 | } | 324 | } |
| 325 | 325 | ||
| 326 | public void removeMethodMapping(MethodMapping methodMapping) { | 326 | public void removeMethodMapping(MethodMapping methodMapping) { |
| 327 | boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; | 327 | boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; |
| 328 | assert (obfWasRemoved); | 328 | assert (obfWasRemoved); |
| 329 | if (methodMapping.getDeobfName() != null) { | 329 | if (methodMapping.getDeobfName() != null) { |
| 330 | boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; | 330 | boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; |
| 331 | assert (deobfWasRemoved); | 331 | assert (deobfWasRemoved); |
| 332 | } | 332 | } |
| 333 | this.isDirty = true; | 333 | this.isDirty = true; |
| 334 | } | 334 | } |
| 335 | 335 | ||
| 336 | public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { | 336 | public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { |
| 337 | return methodsByObf.get(getMethodKey(obfName, obfSignature)); | 337 | return methodsByObf.get(getMethodKey(obfName, obfSignature)); |
| 338 | } | 338 | } |
| 339 | 339 | ||
| 340 | public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { | 340 | public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { |
| 341 | return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); | 341 | return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); |
| 342 | } | 342 | } |
| 343 | 343 | ||
| 344 | private String getMethodKey(String name, Signature signature) { | 344 | private String getMethodKey(String name, Signature signature) { |
| 345 | if (name == null) { | 345 | if (name == null) { |
| 346 | throw new IllegalArgumentException("name cannot be null!"); | 346 | throw new IllegalArgumentException("name cannot be null!"); |
| 347 | } | 347 | } |
| 348 | if (signature == null) { | 348 | if (signature == null) { |
| 349 | throw new IllegalArgumentException("signature cannot be null!"); | 349 | throw new IllegalArgumentException("signature cannot be null!"); |
| 350 | } | 350 | } |
| 351 | return name + signature; | 351 | return name + signature; |
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | public void setMethodName(String obfName, Signature obfSignature, String deobfName) { | 354 | public void setMethodName(String obfName, Signature obfSignature, String deobfName) { |
| 355 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); | 355 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); |
| 356 | if (methodMapping == null) { | 356 | if (methodMapping == null) { |
| 357 | methodMapping = createMethodMapping(obfName, obfSignature); | 357 | methodMapping = createMethodMapping(obfName, obfSignature); |
| 358 | } else if (methodMapping.getDeobfName() != null) { | 358 | } else if (methodMapping.getDeobfName() != null) { |
| 359 | boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; | 359 | boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; |
| 360 | assert (wasRemoved); | 360 | assert (wasRemoved); |
| 361 | } | 361 | } |
| 362 | methodMapping.setDeobfName(deobfName); | 362 | methodMapping.setDeobfName(deobfName); |
| 363 | if (deobfName != null) { | 363 | if (deobfName != null) { |
| 364 | boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; | 364 | boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; |
| 365 | assert (wasAdded); | 365 | assert (wasAdded); |
| 366 | } | 366 | } |
| 367 | this.isDirty = true; | 367 | this.isDirty = true; |
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { | 370 | //// ARGUMENTS //////// |
| 371 | assert(newObfName != null); | 371 | |
| 372 | MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); | 372 | public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { |
| 373 | assert(methodMapping != null); | 373 | assert (newObfName != null); |
| 374 | methodMapping.setObfName(newObfName); | 374 | MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); |
| 375 | methodMapping.setObfSignature(newObfSignature); | 375 | assert (methodMapping != null); |
| 376 | boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; | 376 | methodMapping.setObfName(newObfName); |
| 377 | assert(obfWasAdded); | 377 | methodMapping.setObfSignature(newObfSignature); |
| 378 | this.isDirty = true; | 378 | boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; |
| 379 | } | 379 | assert (obfWasAdded); |
| 380 | 380 | this.isDirty = true; | |
| 381 | //// ARGUMENTS //////// | 381 | } |
| 382 | 382 | ||
| 383 | public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { | 383 | public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { |
| 384 | assert (argumentName != null); | 384 | assert (argumentName != null); |
| 385 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); | 385 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); |
| 386 | if (methodMapping == null) { | 386 | if (methodMapping == null) { |
| 387 | methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); | 387 | methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); |
| 388 | } | 388 | } |
| 389 | methodMapping.setArgumentName(argumentIndex, argumentName); | 389 | methodMapping.setArgumentName(argumentIndex, argumentName); |
| 390 | this.isDirty = true; | 390 | this.isDirty = true; |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { | 393 | public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { |
| 394 | methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); | 394 | methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); |
| 395 | this.isDirty = true; | 395 | this.isDirty = true; |
| 396 | } | 396 | } |
| 397 | 397 | ||
| 398 | private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { | 398 | private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { |
| 399 | MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); | 399 | MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); |
| 400 | boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; | 400 | boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; |
| 401 | assert (wasAdded); | 401 | assert (wasAdded); |
| 402 | this.isDirty = true; | 402 | this.isDirty = true; |
| 403 | return methodMapping; | 403 | return methodMapping; |
| 404 | } | 404 | } |
| 405 | 405 | ||
| 406 | @Override | 406 | @Override |
| 407 | public String toString() { | 407 | public String toString() { |
| 408 | StringBuilder buf = new StringBuilder(); | 408 | StringBuilder buf = new StringBuilder(); |
| 409 | buf.append(obfFullName); | 409 | buf.append(obfFullName); |
| 410 | buf.append(" <-> "); | 410 | buf.append(" <-> "); |
| 411 | buf.append(deobfName); | 411 | buf.append(deobfName); |
| 412 | buf.append("\n"); | 412 | buf.append("\n"); |
| 413 | buf.append("Fields:\n"); | 413 | buf.append("Fields:\n"); |
| 414 | for (FieldMapping fieldMapping : fields()) { | 414 | for (FieldMapping fieldMapping : fields()) { |
| 415 | buf.append("\t"); | 415 | buf.append("\t"); |
| 416 | buf.append(fieldMapping.getObfName()); | 416 | buf.append(fieldMapping.getObfName()); |
| 417 | buf.append(" <-> "); | 417 | buf.append(" <-> "); |
| 418 | buf.append(fieldMapping.getDeobfName()); | 418 | buf.append(fieldMapping.getDeobfName()); |
| 419 | buf.append("\n"); | 419 | buf.append("\n"); |
| 420 | } | 420 | } |
| 421 | buf.append("Methods:\n"); | 421 | buf.append("Methods:\n"); |
| 422 | for (MethodMapping methodMapping : methodsByObf.values()) { | 422 | for (MethodMapping methodMapping : methodsByObf.values()) { |
| 423 | buf.append(methodMapping.toString()); | 423 | buf.append(methodMapping); |
| 424 | buf.append("\n"); | 424 | buf.append("\n"); |
| 425 | } | 425 | } |
| 426 | buf.append("Inner Classes:\n"); | 426 | buf.append("Inner Classes:\n"); |
| 427 | for (ClassMapping classMapping : innerClassesByObfSimple.values()) { | 427 | for (ClassMapping classMapping : innerClassesByObfSimple.values()) { |
| 428 | buf.append("\t"); | 428 | buf.append("\t"); |
| 429 | buf.append(classMapping.getObfSimpleName()); | 429 | buf.append(classMapping.getObfSimpleName()); |
| 430 | buf.append(" <-> "); | 430 | buf.append(" <-> "); |
| 431 | buf.append(classMapping.getDeobfName()); | 431 | buf.append(classMapping.getDeobfName()); |
| 432 | buf.append("\n"); | 432 | buf.append("\n"); |
| 433 | } | 433 | } |
| 434 | return buf.toString(); | 434 | return buf.toString(); |
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | @Override | 437 | @Override |
| 438 | public int compareTo(ClassMapping other) { | 438 | public int compareTo(ClassMapping other) { |
| 439 | // sort by a, b, c, ... aa, ab, etc | 439 | // sort by a, b, c, ... aa, ab, etc |
| 440 | if (obfFullName.length() != other.obfFullName.length()) { | 440 | if (obfFullName.length() != other.obfFullName.length()) { |
| 441 | return obfFullName.length() - other.obfFullName.length(); | 441 | return obfFullName.length() - other.obfFullName.length(); |
| 442 | } | 442 | } |
| 443 | return obfFullName.compareTo(other.obfFullName); | 443 | return obfFullName.compareTo(other.obfFullName); |
| 444 | } | 444 | } |
| 445 | 445 | ||
| 446 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { | 446 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { |
| 447 | 447 | ||
| 448 | // rename inner classes | 448 | // rename inner classes |
| 449 | for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { | 449 | for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { |
| 450 | if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { | 450 | if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { |
| 451 | boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; | 451 | boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; |
| 452 | assert (wasRemoved); | 452 | assert (wasRemoved); |
| 453 | boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; | 453 | boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; |
| 454 | assert (wasAdded); | 454 | assert (wasAdded); |
| 455 | } | 455 | } |
| 456 | } | 456 | } |
| 457 | 457 | ||
| 458 | // rename field types | 458 | // rename field types |
| 459 | for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { | 459 | for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { |
| 460 | String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | 460 | String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); |
| 461 | if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { | 461 | if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { |
| 462 | boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; | 462 | boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; |
| 463 | assert (wasRemoved); | 463 | assert (wasRemoved); |
| 464 | boolean wasAdded = fieldsByObf | 464 | boolean wasAdded = fieldsByObf |
| 465 | .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; | 465 | .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; |
| 466 | assert (wasAdded); | 466 | assert (wasAdded); |
| 467 | } | 467 | } |
| 468 | } | 468 | } |
| 469 | 469 | ||
| 470 | // rename method signatures | 470 | // rename method signatures |
| 471 | for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { | 471 | for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { |
| 472 | String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | 472 | String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); |
| 473 | if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { | 473 | if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { |
| 474 | boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; | 474 | boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; |
| 475 | assert (wasRemoved); | 475 | assert (wasRemoved); |
| 476 | boolean wasAdded = methodsByObf | 476 | boolean wasAdded = methodsByObf |
| 477 | .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; | 477 | .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; |
| 478 | assert (wasAdded); | 478 | assert (wasAdded); |
| 479 | } | 479 | } |
| 480 | } | 480 | } |
| 481 | 481 | ||
| 482 | if (obfFullName.equals(oldObfClassName)) { | 482 | if (obfFullName.equals(oldObfClassName)) { |
| 483 | // rename this class | 483 | // rename this class |
| 484 | obfFullName = newObfClassName; | 484 | obfFullName = newObfClassName; |
| 485 | return true; | 485 | return true; |
| 486 | } | 486 | } |
| 487 | this.isDirty = true; | 487 | this.isDirty = true; |
| 488 | return false; | 488 | return false; |
| 489 | } | 489 | } |
| 490 | 490 | ||
| 491 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { | 491 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { |
| 492 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); | 492 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); |
| 493 | return methodMapping != null && methodMapping.containsArgument(name); | 493 | return methodMapping != null && methodMapping.containsArgument(name); |
| 494 | } | 494 | } |
| 495 | 495 | ||
| 496 | public static boolean isSimpleClassName(String name) { | 496 | public ClassEntry getObfEntry() { |
| 497 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; | 497 | return new ClassEntry(obfFullName); |
| 498 | } | 498 | } |
| 499 | 499 | ||
| 500 | public ClassEntry getObfEntry() { | 500 | public boolean isDirty() { |
| 501 | return new ClassEntry(obfFullName); | 501 | return isDirty; |
| 502 | } | 502 | } |
| 503 | 503 | ||
| 504 | public boolean isDirty() | 504 | public void resetDirty() { |
| 505 | { | 505 | this.isDirty = false; |
| 506 | return isDirty; | 506 | } |
| 507 | } | 507 | |
| 508 | 508 | public Mappings.EntryModifier getModifier() { | |
| 509 | public void resetDirty() | 509 | return modifier; |
| 510 | { | 510 | } |
| 511 | this.isDirty = false; | 511 | |
| 512 | } | 512 | public void setModifier(Mappings.EntryModifier modifier) { |
| 513 | 513 | if (this.modifier != modifier) | |
| 514 | public void setModifier(Mappings.EntryModifier modifier) | 514 | this.isDirty = true; |
| 515 | { | 515 | this.modifier = modifier; |
| 516 | if (this.modifier != modifier) | 516 | } |
| 517 | this.isDirty = true; | 517 | |
| 518 | this.modifier = modifier; | 518 | public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { |
| 519 | } | 519 | FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), |
| 520 | 520 | k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); | |
| 521 | public Mappings.EntryModifier getModifier() | 521 | |
| 522 | { | 522 | if (fieldMapping.getModifier() != modifier) { |
| 523 | return modifier; | 523 | fieldMapping.setModifier(modifier); |
| 524 | } | 524 | this.isDirty = true; |
| 525 | 525 | } | |
| 526 | public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { | 526 | } |
| 527 | FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), | 527 | |
| 528 | k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); | 528 | public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { |
| 529 | 529 | MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), | |
| 530 | if (fieldMapping.getModifier() != modifier) | 530 | k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); |
| 531 | { | 531 | |
| 532 | fieldMapping.setModifier(modifier); | 532 | if (methodMapping.getModifier() != modifier) { |
| 533 | this.isDirty = true; | 533 | methodMapping.setModifier(modifier); |
| 534 | } | 534 | this.isDirty = true; |
| 535 | } | 535 | } |
| 536 | 536 | } | |
| 537 | public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { | ||
| 538 | MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), | ||
| 539 | k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); | ||
| 540 | |||
| 541 | if (methodMapping.getModifier() != modifier) | ||
| 542 | { | ||
| 543 | methodMapping.setModifier(modifier); | ||
| 544 | this.isDirty = true; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | } | 537 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java index dc833bb8..801c4104 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java | |||
| @@ -8,8 +8,9 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | public interface ClassNameReplacer { | 14 | public interface ClassNameReplacer { |
| 14 | String replace(String className); | 15 | String replace(String className); |
| 15 | } | 16 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java index 4c798204..20e51138 100644 --- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java | |||
| @@ -8,97 +8,98 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.utils.Utils; | 14 | import cuchaz.enigma.utils.Utils; |
| 14 | 15 | ||
| 15 | public class ConstructorEntry implements BehaviorEntry { | 16 | public class ConstructorEntry implements BehaviorEntry { |
| 16 | 17 | ||
| 17 | private ClassEntry classEntry; | 18 | private ClassEntry classEntry; |
| 18 | private Signature signature; | 19 | private Signature signature; |
| 19 | 20 | ||
| 20 | public ConstructorEntry(ClassEntry classEntry) { | 21 | public ConstructorEntry(ClassEntry classEntry) { |
| 21 | this(classEntry, null); | 22 | this(classEntry, null); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | public ConstructorEntry(ClassEntry classEntry, Signature signature) { | 25 | public ConstructorEntry(ClassEntry classEntry, Signature signature) { |
| 25 | if (classEntry == null) { | 26 | if (classEntry == null) { |
| 26 | throw new IllegalArgumentException("Class cannot be null!"); | 27 | throw new IllegalArgumentException("Class cannot be null!"); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | this.classEntry = classEntry; | 30 | this.classEntry = classEntry; |
| 30 | this.signature = signature; | 31 | this.signature = signature; |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | public ConstructorEntry(ConstructorEntry other, String newClassName) { | 34 | public ConstructorEntry(ConstructorEntry other, String newClassName) { |
| 34 | this.classEntry = new ClassEntry(newClassName); | 35 | this.classEntry = new ClassEntry(newClassName); |
| 35 | this.signature = other.signature; | 36 | this.signature = other.signature; |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | @Override | 39 | @Override |
| 39 | public ClassEntry getClassEntry() { | 40 | public ClassEntry getClassEntry() { |
| 40 | return this.classEntry; | 41 | return this.classEntry; |
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | @Override | 44 | @Override |
| 44 | public String getName() { | 45 | public String getName() { |
| 45 | if (isStatic()) { | 46 | if (isStatic()) { |
| 46 | return "<clinit>"; | 47 | return "<clinit>"; |
| 47 | } | 48 | } |
| 48 | return "<init>"; | 49 | return "<init>"; |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | public boolean isStatic() { | 52 | public boolean isStatic() { |
| 52 | return this.signature == null; | 53 | return this.signature == null; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | @Override | 56 | @Override |
| 56 | public Signature getSignature() { | 57 | public Signature getSignature() { |
| 57 | return this.signature; | 58 | return this.signature; |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | @Override | 61 | @Override |
| 61 | public String getClassName() { | 62 | public String getClassName() { |
| 62 | return this.classEntry.getName(); | 63 | return this.classEntry.getName(); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | @Override | 66 | @Override |
| 66 | public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { | 67 | public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { |
| 67 | return new ConstructorEntry(this, classEntry.getName()); | 68 | return new ConstructorEntry(this, classEntry.getName()); |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | @Override | 71 | @Override |
| 71 | public int hashCode() { | 72 | public int hashCode() { |
| 72 | if (isStatic()) { | 73 | if (isStatic()) { |
| 73 | return Utils.combineHashesOrdered(this.classEntry); | 74 | return Utils.combineHashesOrdered(this.classEntry); |
| 74 | } else { | 75 | } else { |
| 75 | return Utils.combineHashesOrdered(this.classEntry, this.signature); | 76 | return Utils.combineHashesOrdered(this.classEntry, this.signature); |
| 76 | } | 77 | } |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | @Override | 80 | @Override |
| 80 | public boolean equals(Object other) { | 81 | public boolean equals(Object other) { |
| 81 | return other instanceof ConstructorEntry && equals((ConstructorEntry) other); | 82 | return other instanceof ConstructorEntry && equals((ConstructorEntry) other); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | public boolean equals(ConstructorEntry other) { | 85 | public boolean equals(ConstructorEntry other) { |
| 85 | if (isStatic() != other.isStatic()) { | 86 | if (isStatic() != other.isStatic()) { |
| 86 | return false; | 87 | return false; |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 89 | if (isStatic()) { | 90 | if (isStatic()) { |
| 90 | return this.classEntry.equals(other.classEntry); | 91 | return this.classEntry.equals(other.classEntry); |
| 91 | } else { | 92 | } else { |
| 92 | return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); | 93 | return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); |
| 93 | } | 94 | } |
| 94 | } | 95 | } |
| 95 | 96 | ||
| 96 | @Override | 97 | @Override |
| 97 | public String toString() { | 98 | public String toString() { |
| 98 | if (isStatic()) { | 99 | if (isStatic()) { |
| 99 | return this.classEntry.getName() + "." + getName(); | 100 | return this.classEntry.getName() + "." + getName(); |
| 100 | } else { | 101 | } else { |
| 101 | return this.classEntry.getName() + "." + getName() + this.signature; | 102 | return this.classEntry.getName() + "." + getName() + this.signature; |
| 102 | } | 103 | } |
| 103 | } | 104 | } |
| 104 | } | 105 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java index 95747d55..c79510b9 100644 --- a/src/main/java/cuchaz/enigma/mapping/Entry.java +++ b/src/main/java/cuchaz/enigma/mapping/Entry.java | |||
| @@ -8,14 +8,15 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | public interface Entry { | 14 | public interface Entry { |
| 14 | String getName(); | 15 | String getName(); |
| 15 | 16 | ||
| 16 | String getClassName(); | 17 | String getClassName(); |
| 17 | 18 | ||
| 18 | ClassEntry getClassEntry(); | 19 | ClassEntry getClassEntry(); |
| 19 | 20 | ||
| 20 | Entry cloneToNewClass(ClassEntry classEntry); | 21 | Entry cloneToNewClass(ClassEntry classEntry); |
| 21 | } | 22 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java index ce4b948a..993bb64b 100644 --- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.analysis.JarIndex; | 14 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -20,112 +21,112 @@ import javassist.expr.NewExpr; | |||
| 20 | 21 | ||
| 21 | public class EntryFactory { | 22 | public class EntryFactory { |
| 22 | 23 | ||
| 23 | public static ClassEntry getClassEntry(CtClass c) { | 24 | public static ClassEntry getClassEntry(CtClass c) { |
| 24 | return new ClassEntry(Descriptor.toJvmName(c.getName())); | 25 | return new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { | 28 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { |
| 28 | ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); | 29 | ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); |
| 29 | return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); | 30 | return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | private static ClassEntry getObfClassEntry(ClassMapping classMapping) { | 33 | private static ClassEntry getObfClassEntry(ClassMapping classMapping) { |
| 33 | return new ClassEntry(classMapping.getObfFullName()); | 34 | return new ClassEntry(classMapping.getObfFullName()); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | 37 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { |
| 37 | return new ClassEntry(classMapping.getDeobfName()); | 38 | return new ClassEntry(classMapping.getDeobfName()); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | public static ClassEntry getSuperclassEntry(CtClass c) { | 41 | public static ClassEntry getSuperclassEntry(CtClass c) { |
| 41 | return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); | 42 | return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | public static FieldEntry getFieldEntry(CtField field) { | 45 | public static FieldEntry getFieldEntry(CtField field) { |
| 45 | return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); | 46 | return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | public static FieldEntry getFieldEntry(FieldAccess call) { | 49 | public static FieldEntry getFieldEntry(FieldAccess call) { |
| 49 | return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); | 50 | return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | public static FieldEntry getFieldEntry(String className, String name, String type) { | 53 | public static FieldEntry getFieldEntry(String className, String name, String type) { |
| 53 | return new FieldEntry(new ClassEntry(className), name, new Type(type)); | 54 | return new FieldEntry(new ClassEntry(className), name, new Type(type)); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { | 57 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { |
| 57 | return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); | 58 | return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | public static MethodEntry getMethodEntry(CtMethod method) { | 61 | public static MethodEntry getMethodEntry(CtMethod method) { |
| 61 | return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); | 62 | return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | public static MethodEntry getMethodEntry(MethodCall call) { | 65 | public static MethodEntry getMethodEntry(MethodCall call) { |
| 65 | return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); | 66 | return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { | 69 | public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { |
| 69 | if (constructor.isClassInitializer()) { | 70 | if (constructor.isClassInitializer()) { |
| 70 | return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); | 71 | return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); |
| 71 | } else { | 72 | } else { |
| 72 | return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); | 73 | return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); |
| 73 | } | 74 | } |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | public static ConstructorEntry getConstructorEntry(ConstructorCall call) { | 77 | public static ConstructorEntry getConstructorEntry(ConstructorCall call) { |
| 77 | return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); | 78 | return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); |
| 78 | } | 79 | } |
| 79 | 80 | ||
| 80 | public static ConstructorEntry getConstructorEntry(NewExpr call) { | 81 | public static ConstructorEntry getConstructorEntry(NewExpr call) { |
| 81 | return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); | 82 | return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { | 85 | public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { |
| 85 | if (behavior instanceof CtMethod) { | 86 | if (behavior instanceof CtMethod) { |
| 86 | return getMethodEntry((CtMethod) behavior); | 87 | return getMethodEntry((CtMethod) behavior); |
| 87 | } else if (behavior instanceof CtConstructor) { | 88 | } else if (behavior instanceof CtConstructor) { |
| 88 | return getConstructorEntry((CtConstructor) behavior); | 89 | return getConstructorEntry((CtConstructor) behavior); |
| 89 | } | 90 | } |
| 90 | throw new Error("behavior is neither Method nor Constructor!"); | 91 | throw new Error("behavior is neither Method nor Constructor!"); |
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { | 94 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { |
| 94 | return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); | 95 | return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { | 98 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { |
| 98 | return getBehaviorEntry(new ClassEntry(className), behaviorName); | 99 | return getBehaviorEntry(new ClassEntry(className), behaviorName); |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | public static BehaviorEntry getBehaviorEntry(String className) { | 102 | public static BehaviorEntry getBehaviorEntry(String className) { |
| 102 | return new ConstructorEntry(new ClassEntry(className)); | 103 | return new ConstructorEntry(new ClassEntry(className)); |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { | 106 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { |
| 106 | switch (behaviorName) { | 107 | switch (behaviorName) { |
| 107 | case "<init>": | 108 | case "<init>": |
| 108 | return new ConstructorEntry(classEntry, behaviorSignature); | 109 | return new ConstructorEntry(classEntry, behaviorSignature); |
| 109 | case "<clinit>": | 110 | case "<clinit>": |
| 110 | return new ConstructorEntry(classEntry); | 111 | return new ConstructorEntry(classEntry); |
| 111 | default: | 112 | default: |
| 112 | return new MethodEntry(classEntry, behaviorName, behaviorSignature); | 113 | return new MethodEntry(classEntry, behaviorName, behaviorSignature); |
| 113 | } | 114 | } |
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { | 117 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { |
| 117 | if(behaviorName.equals("<clinit>")) { | 118 | if (behaviorName.equals("<clinit>")) { |
| 118 | return new ConstructorEntry(classEntry); | 119 | return new ConstructorEntry(classEntry); |
| 119 | } else { | 120 | } else { |
| 120 | throw new IllegalArgumentException("Only class initializers don't have signatures"); | 121 | throw new IllegalArgumentException("Only class initializers don't have signatures"); |
| 121 | } | 122 | } |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { | 125 | public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { |
| 125 | return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); | 126 | return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 128 | public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { | 129 | public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { |
| 129 | return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); | 130 | return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); |
| 130 | } | 131 | } |
| 131 | } | 132 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java index 9980e8e6..0f1f5065 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java | |||
| @@ -8,79 +8,80 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.utils.Utils; | 14 | import cuchaz.enigma.utils.Utils; |
| 14 | 15 | ||
| 15 | public class FieldEntry implements Entry { | 16 | public class FieldEntry implements Entry { |
| 16 | 17 | ||
| 17 | private ClassEntry classEntry; | 18 | private ClassEntry classEntry; |
| 18 | private String name; | 19 | private String name; |
| 19 | private Type type; | 20 | private Type type; |
| 20 | 21 | ||
| 21 | // NOTE: this argument order is important for the MethodReader/MethodWriter | 22 | // NOTE: this argument order is important for the MethodReader/MethodWriter |
| 22 | public FieldEntry(ClassEntry classEntry, String name, Type type) { | 23 | public FieldEntry(ClassEntry classEntry, String name, Type type) { |
| 23 | if (classEntry == null) { | 24 | if (classEntry == null) { |
| 24 | throw new IllegalArgumentException("Class cannot be null!"); | 25 | throw new IllegalArgumentException("Class cannot be null!"); |
| 25 | } | 26 | } |
| 26 | if (name == null) { | 27 | if (name == null) { |
| 27 | throw new IllegalArgumentException("Field name cannot be null!"); | 28 | throw new IllegalArgumentException("Field name cannot be null!"); |
| 28 | } | 29 | } |
| 29 | if (type == null) { | 30 | if (type == null) { |
| 30 | throw new IllegalArgumentException("Field type cannot be null!"); | 31 | throw new IllegalArgumentException("Field type cannot be null!"); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | this.classEntry = classEntry; | 34 | this.classEntry = classEntry; |
| 34 | this.name = name; | 35 | this.name = name; |
| 35 | this.type = type; | 36 | this.type = type; |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { | 39 | public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { |
| 39 | this.classEntry = newClassEntry; | 40 | this.classEntry = newClassEntry; |
| 40 | this.name = other.name; | 41 | this.name = other.name; |
| 41 | this.type = other.type; | 42 | this.type = other.type; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | @Override | 45 | @Override |
| 45 | public ClassEntry getClassEntry() { | 46 | public ClassEntry getClassEntry() { |
| 46 | return this.classEntry; | 47 | return this.classEntry; |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | @Override | 50 | @Override |
| 50 | public String getName() { | 51 | public String getName() { |
| 51 | return this.name; | 52 | return this.name; |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | @Override | 55 | @Override |
| 55 | public String getClassName() { | 56 | public String getClassName() { |
| 56 | return this.classEntry.getName(); | 57 | return this.classEntry.getName(); |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | public Type getType() { | 60 | public Type getType() { |
| 60 | return this.type; | 61 | return this.type; |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | @Override | 64 | @Override |
| 64 | public FieldEntry cloneToNewClass(ClassEntry classEntry) { | 65 | public FieldEntry cloneToNewClass(ClassEntry classEntry) { |
| 65 | return new FieldEntry(this, classEntry); | 66 | return new FieldEntry(this, classEntry); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | @Override | 69 | @Override |
| 69 | public int hashCode() { | 70 | public int hashCode() { |
| 70 | return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); | 71 | return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | @Override | 74 | @Override |
| 74 | public boolean equals(Object other) { | 75 | public boolean equals(Object other) { |
| 75 | return other instanceof FieldEntry && equals((FieldEntry) other); | 76 | return other instanceof FieldEntry && equals((FieldEntry) other); |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | public boolean equals(FieldEntry other) { | 79 | public boolean equals(FieldEntry other) { |
| 79 | return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); | 80 | return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | @Override | 83 | @Override |
| 83 | public String toString() { | 84 | public String toString() { |
| 84 | return this.classEntry.getName() + "." + this.name + ":" + this.type; | 85 | return this.classEntry.getName() + "." + this.name + ":" + this.type; |
| 85 | } | 86 | } |
| 86 | } | 87 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java index 22ba307e..cd761b47 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java | |||
| @@ -8,103 +8,99 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.throwables.IllegalNameException; | 14 | import cuchaz.enigma.throwables.IllegalNameException; |
| 14 | 15 | ||
| 15 | public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { | 16 | public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { |
| 16 | 17 | ||
| 17 | private String obfName; | 18 | private String obfName; |
| 18 | private String deobfName; | 19 | private String deobfName; |
| 19 | private Type obfType; | 20 | private Type obfType; |
| 20 | private Mappings.EntryModifier modifier; | 21 | private Mappings.EntryModifier modifier; |
| 21 | 22 | ||
| 22 | public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { | 23 | public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { |
| 23 | this.obfName = obfName; | 24 | this.obfName = obfName; |
| 24 | this.deobfName = NameValidator.validateFieldName(deobfName); | 25 | this.deobfName = NameValidator.validateFieldName(deobfName); |
| 25 | this.obfType = obfType; | 26 | this.obfType = obfType; |
| 26 | this.modifier = modifier; | 27 | this.modifier = modifier; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { | 30 | public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { |
| 30 | this.obfName = other.obfName; | 31 | this.obfName = other.obfName; |
| 31 | this.deobfName = other.deobfName; | 32 | this.deobfName = other.deobfName; |
| 32 | this.modifier = other.modifier; | 33 | this.modifier = other.modifier; |
| 33 | this.obfType = new Type(other.obfType, obfClassNameReplacer); | 34 | this.obfType = new Type(other.obfType, obfClassNameReplacer); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | @Override | 37 | @Override |
| 37 | public FieldEntry getObfEntry(ClassEntry classEntry) { | 38 | public FieldEntry getObfEntry(ClassEntry classEntry) { |
| 38 | return new FieldEntry(classEntry, this.obfName, this.obfType); | 39 | return new FieldEntry(classEntry, this.obfName, this.obfType); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | @Override | 42 | @Override |
| 42 | public String getObfName() { | 43 | public String getObfName() { |
| 43 | return this.obfName; | 44 | return this.obfName; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | public String getDeobfName() { | 47 | public void setObfName(String name) { |
| 47 | return this.deobfName; | 48 | try { |
| 48 | } | 49 | NameValidator.validateMethodName(name); |
| 49 | 50 | } catch (IllegalNameException ex) { | |
| 50 | public void setDeobfName(String val) { | 51 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues |
| 51 | this.deobfName = NameValidator.validateFieldName(val); | 52 | if (this.deobfName == null) { |
| 52 | } | 53 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); |
| 53 | 54 | setDeobfName(name + "_auto_deob"); | |
| 54 | public void setObfName(String name) { | 55 | } |
| 55 | try | 56 | } |
| 56 | { | 57 | this.obfName = name; |
| 57 | NameValidator.validateMethodName(name); | 58 | } |
| 58 | } catch (IllegalNameException ex) | 59 | |
| 59 | { | 60 | public String getDeobfName() { |
| 60 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues | 61 | return this.deobfName; |
| 61 | if (this.deobfName == null) | 62 | } |
| 62 | { | 63 | |
| 63 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); | 64 | public void setDeobfName(String val) { |
| 64 | setDeobfName(name + "_auto_deob"); | 65 | this.deobfName = NameValidator.validateFieldName(val); |
| 65 | } | 66 | } |
| 66 | } | 67 | |
| 67 | this.obfName = name; | 68 | public Type getObfType() { |
| 68 | } | 69 | return this.obfType; |
| 69 | 70 | } | |
| 70 | public Type getObfType() { | 71 | |
| 71 | return this.obfType; | 72 | public void setObfType(Type val) { |
| 72 | } | 73 | this.obfType = val; |
| 73 | 74 | } | |
| 74 | public void setObfType(Type val) { | 75 | |
| 75 | this.obfType = val; | 76 | public Mappings.EntryModifier getModifier() { |
| 76 | } | 77 | return modifier; |
| 77 | 78 | } | |
| 78 | public void setModifier(Mappings.EntryModifier modifier) | 79 | |
| 79 | { | 80 | public void setModifier(Mappings.EntryModifier modifier) { |
| 80 | this.modifier = modifier; | 81 | this.modifier = modifier; |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | public Mappings.EntryModifier getModifier() | 84 | @Override |
| 84 | { | 85 | public int compareTo(FieldMapping other) { |
| 85 | return modifier; | 86 | return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | @Override | 89 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { |
| 89 | public int compareTo(FieldMapping other) { | 90 | // rename obf classes in the type |
| 90 | return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); | 91 | Type newType = new Type(this.obfType, className -> |
| 91 | } | 92 | { |
| 92 | 93 | if (className.equals(oldObfClassName)) { | |
| 93 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | 94 | return newObfClassName; |
| 94 | // rename obf classes in the type | 95 | } |
| 95 | Type newType = new Type(this.obfType, className -> | 96 | return null; |
| 96 | { | 97 | }); |
| 97 | if (className.equals(oldObfClassName)) { | 98 | |
| 98 | return newObfClassName; | 99 | if (!newType.equals(this.obfType)) { |
| 99 | } | 100 | this.obfType = newType; |
| 100 | return null; | 101 | return true; |
| 101 | }); | 102 | } |
| 102 | 103 | return false; | |
| 103 | if (!newType.equals(this.obfType)) { | 104 | } |
| 104 | this.obfType = newType; | ||
| 105 | return true; | ||
| 106 | } | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | 105 | ||
| 110 | } | 106 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java index 8bbaaaf6..2bb5e3f7 100644 --- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java | |||
| @@ -7,98 +7,96 @@ import cuchaz.enigma.utils.Utils; | |||
| 7 | * Created by Thog | 7 | * Created by Thog |
| 8 | * 19/10/2016 | 8 | * 19/10/2016 |
| 9 | */ | 9 | */ |
| 10 | public class LocalVariableEntry implements Entry | 10 | public class LocalVariableEntry implements Entry { |
| 11 | { | 11 | |
| 12 | 12 | protected final BehaviorEntry behaviorEntry; | |
| 13 | protected final BehaviorEntry behaviorEntry; | 13 | protected final String name; |
| 14 | protected final String name; | 14 | protected final Type type; |
| 15 | protected final Type type; | 15 | protected final int index; |
| 16 | protected final int index; | 16 | |
| 17 | 17 | public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { | |
| 18 | public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { | 18 | if (behaviorEntry == null) { |
| 19 | if (behaviorEntry == null) { | 19 | throw new IllegalArgumentException("Behavior cannot be null!"); |
| 20 | throw new IllegalArgumentException("Behavior cannot be null!"); | 20 | } |
| 21 | } | 21 | if (index < 0) { |
| 22 | if (index < 0) { | 22 | throw new IllegalArgumentException("Index must be non-negative!"); |
| 23 | throw new IllegalArgumentException("Index must be non-negative!"); | 23 | } |
| 24 | } | 24 | if (name == null) { |
| 25 | if (name == null) { | 25 | throw new IllegalArgumentException("Variable name cannot be null!"); |
| 26 | throw new IllegalArgumentException("Variable name cannot be null!"); | 26 | } |
| 27 | } | 27 | if (type == null) { |
| 28 | if (type == null) { | 28 | throw new IllegalArgumentException("Variable type cannot be null!"); |
| 29 | throw new IllegalArgumentException("Variable type cannot be null!"); | 29 | } |
| 30 | } | 30 | |
| 31 | 31 | this.behaviorEntry = behaviorEntry; | |
| 32 | this.behaviorEntry = behaviorEntry; | 32 | this.name = name; |
| 33 | this.name = name; | 33 | this.type = type; |
| 34 | this.type = type; | 34 | this.index = index; |
| 35 | this.index = index; | 35 | } |
| 36 | } | 36 | |
| 37 | 37 | public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { | |
| 38 | 38 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); | |
| 39 | public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { | 39 | this.name = other.name; |
| 40 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); | 40 | this.type = other.type; |
| 41 | this.name = other.name; | 41 | this.index = other.index; |
| 42 | this.type = other.type; | 42 | } |
| 43 | this.index = other.index; | 43 | |
| 44 | } | 44 | public BehaviorEntry getBehaviorEntry() { |
| 45 | 45 | return this.behaviorEntry; | |
| 46 | public BehaviorEntry getBehaviorEntry() { | 46 | } |
| 47 | return this.behaviorEntry; | 47 | |
| 48 | } | 48 | public Type getType() { |
| 49 | 49 | return type; | |
| 50 | public Type getType() { | 50 | } |
| 51 | return type; | 51 | |
| 52 | } | 52 | public int getIndex() { |
| 53 | 53 | return index; | |
| 54 | public int getIndex() { | 54 | } |
| 55 | return index; | 55 | |
| 56 | } | 56 | @Override |
| 57 | 57 | public String getName() { | |
| 58 | @Override | 58 | return this.name; |
| 59 | public String getName() { | 59 | } |
| 60 | return this.name; | 60 | |
| 61 | } | 61 | @Override |
| 62 | 62 | public ClassEntry getClassEntry() { | |
| 63 | @Override | 63 | return this.behaviorEntry.getClassEntry(); |
| 64 | public ClassEntry getClassEntry() { | 64 | } |
| 65 | return this.behaviorEntry.getClassEntry(); | 65 | |
| 66 | } | 66 | @Override |
| 67 | 67 | public String getClassName() { | |
| 68 | @Override | 68 | return this.behaviorEntry.getClassName(); |
| 69 | public String getClassName() { | 69 | } |
| 70 | return this.behaviorEntry.getClassName(); | 70 | |
| 71 | } | 71 | @Override |
| 72 | 72 | public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { | |
| 73 | @Override | 73 | return new LocalVariableEntry(this, classEntry); |
| 74 | public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { | 74 | } |
| 75 | return new LocalVariableEntry(this, classEntry); | 75 | |
| 76 | } | 76 | public String getMethodName() { |
| 77 | 77 | return this.behaviorEntry.getName(); | |
| 78 | public String getMethodName() { | 78 | } |
| 79 | return this.behaviorEntry.getName(); | 79 | |
| 80 | } | 80 | public Signature getMethodSignature() { |
| 81 | 81 | return this.behaviorEntry.getSignature(); | |
| 82 | public Signature getMethodSignature() { | 82 | } |
| 83 | return this.behaviorEntry.getSignature(); | 83 | |
| 84 | } | 84 | @Override |
| 85 | 85 | public int hashCode() { | |
| 86 | @Override | 86 | return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); |
| 87 | public int hashCode() { | 87 | } |
| 88 | return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); | 88 | |
| 89 | } | 89 | @Override |
| 90 | 90 | public boolean equals(Object other) { | |
| 91 | @Override | 91 | return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); |
| 92 | public boolean equals(Object other) { | 92 | } |
| 93 | return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); | 93 | |
| 94 | } | 94 | public boolean equals(LocalVariableEntry other) { |
| 95 | 95 | return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; | |
| 96 | public boolean equals(LocalVariableEntry other) { | 96 | } |
| 97 | return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; | 97 | |
| 98 | } | 98 | @Override |
| 99 | 99 | public String toString() { | |
| 100 | @Override | 100 | return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")"; |
| 101 | public String toString() { | 101 | } |
| 102 | return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ":" + this.type + ")"; | ||
| 103 | } | ||
| 104 | } | 102 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java index d493dcfa..33dd3c54 100644 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java | |||
| @@ -8,246 +8,237 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 15 | 19 | ||
| 16 | import java.io.File; | 20 | import java.io.File; |
| 17 | import java.io.IOException; | 21 | import java.io.IOException; |
| 18 | import java.util.*; | 22 | import java.util.*; |
| 19 | 23 | ||
| 20 | import com.google.common.collect.Sets; | ||
| 21 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 22 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 23 | |||
| 24 | public class Mappings { | 24 | public class Mappings { |
| 25 | 25 | ||
| 26 | protected Map<String, ClassMapping> classesByObf; | 26 | private final FormatType originMapping; |
| 27 | protected Map<String, ClassMapping> classesByDeobf; | 27 | protected Map<String, ClassMapping> classesByObf; |
| 28 | private final FormatType originMapping; | 28 | protected Map<String, ClassMapping> classesByDeobf; |
| 29 | private Mappings previousState; | 29 | private Mappings previousState; |
| 30 | 30 | ||
| 31 | public Mappings() | 31 | public Mappings() { |
| 32 | { | 32 | this(FormatType.ENIGMA_DIRECTORY); |
| 33 | this(FormatType.ENIGMA_DIRECTORY); | 33 | } |
| 34 | } | 34 | |
| 35 | 35 | public Mappings(FormatType originMapping) { | |
| 36 | public Mappings(FormatType originMapping) { | 36 | this.originMapping = originMapping; |
| 37 | this.originMapping = originMapping; | 37 | this.classesByObf = Maps.newHashMap(); |
| 38 | this.classesByObf = Maps.newHashMap(); | 38 | this.classesByDeobf = Maps.newHashMap(); |
| 39 | this.classesByDeobf = Maps.newHashMap(); | 39 | this.previousState = null; |
| 40 | this.previousState = null; | 40 | } |
| 41 | } | 41 | |
| 42 | 42 | public Collection<ClassMapping> classes() { | |
| 43 | public Collection<ClassMapping> classes() { | 43 | assert (this.classesByObf.size() >= this.classesByDeobf.size()); |
| 44 | assert (this.classesByObf.size() >= this.classesByDeobf.size()); | 44 | return this.classesByObf.values(); |
| 45 | return this.classesByObf.values(); | 45 | } |
| 46 | } | 46 | |
| 47 | 47 | public void addClassMapping(ClassMapping classMapping) throws MappingConflict { | |
| 48 | public void addClassMapping(ClassMapping classMapping) throws MappingConflict { | 48 | if (this.classesByObf.containsKey(classMapping.getObfFullName())) { |
| 49 | if (this.classesByObf.containsKey(classMapping.getObfFullName())) { | 49 | throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); |
| 50 | throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); | 50 | } |
| 51 | } | 51 | this.classesByObf.put(classMapping.getObfFullName(), classMapping); |
| 52 | this.classesByObf.put(classMapping.getObfFullName(), classMapping); | 52 | |
| 53 | 53 | if (classMapping.getDeobfName() != null) { | |
| 54 | if (classMapping.getDeobfName() != null) { | 54 | if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { |
| 55 | if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { | 55 | throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); |
| 56 | throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); | 56 | } |
| 57 | } | 57 | this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); |
| 58 | this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); | 58 | } |
| 59 | } | 59 | } |
| 60 | } | 60 | |
| 61 | 61 | public void removeClassMapping(ClassMapping classMapping) { | |
| 62 | public void removeClassMapping(ClassMapping classMapping) { | 62 | boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; |
| 63 | boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; | 63 | assert (obfWasRemoved); |
| 64 | assert (obfWasRemoved); | 64 | if (classMapping.getDeobfName() != null) { |
| 65 | if (classMapping.getDeobfName() != null) { | 65 | boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; |
| 66 | boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; | 66 | assert (deobfWasRemoved); |
| 67 | assert (deobfWasRemoved); | 67 | } |
| 68 | } | 68 | } |
| 69 | } | 69 | |
| 70 | 70 | public ClassMapping getClassByObf(ClassEntry entry) { | |
| 71 | 71 | return getClassByObf(entry.getName()); | |
| 72 | public ClassMapping getClassByObf(ClassEntry entry) { | 72 | } |
| 73 | return getClassByObf(entry.getName()); | 73 | |
| 74 | } | 74 | public ClassMapping getClassByObf(String obfName) { |
| 75 | 75 | return this.classesByObf.get(obfName); | |
| 76 | public ClassMapping getClassByObf(String obfName) { | 76 | } |
| 77 | return this.classesByObf.get(obfName); | 77 | |
| 78 | } | 78 | public ClassMapping getClassByDeobf(ClassEntry entry) { |
| 79 | 79 | return getClassByDeobf(entry.getName()); | |
| 80 | public ClassMapping getClassByDeobf(ClassEntry entry) { | 80 | } |
| 81 | return getClassByDeobf(entry.getName()); | 81 | |
| 82 | } | 82 | public ClassMapping getClassByDeobf(String deobfName) { |
| 83 | 83 | return this.classesByDeobf.get(deobfName); | |
| 84 | public ClassMapping getClassByDeobf(String deobfName) { | 84 | } |
| 85 | return this.classesByDeobf.get(deobfName); | 85 | |
| 86 | } | 86 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { |
| 87 | 87 | if (classMapping.getDeobfName() != null) { | |
| 88 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { | 88 | boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; |
| 89 | if (classMapping.getDeobfName() != null) { | 89 | assert (wasRemoved); |
| 90 | boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; | 90 | } |
| 91 | assert (wasRemoved); | 91 | classMapping.setDeobfName(deobfName); |
| 92 | } | 92 | if (deobfName != null) { |
| 93 | classMapping.setDeobfName(deobfName); | 93 | boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; |
| 94 | if (deobfName != null) { | 94 | assert (wasAdded); |
| 95 | boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; | 95 | } |
| 96 | assert (wasAdded); | 96 | } |
| 97 | } | 97 | |
| 98 | } | 98 | public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { |
| 99 | 99 | switch (direction) { | |
| 100 | public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { | 100 | case Deobfuscating: |
| 101 | switch (direction) { | 101 | |
| 102 | case Deobfuscating: | 102 | return new Translator(direction, this.classesByObf, index); |
| 103 | 103 | ||
| 104 | return new Translator(direction, this.classesByObf, index); | 104 | case Obfuscating: |
| 105 | 105 | ||
| 106 | case Obfuscating: | 106 | // fill in the missing deobf class entries with obf entries |
| 107 | 107 | Map<String, ClassMapping> classes = Maps.newHashMap(); | |
| 108 | // fill in the missing deobf class entries with obf entries | 108 | for (ClassMapping classMapping : classes()) { |
| 109 | Map<String, ClassMapping> classes = Maps.newHashMap(); | 109 | if (classMapping.getDeobfName() != null) { |
| 110 | for (ClassMapping classMapping : classes()) { | 110 | classes.put(classMapping.getDeobfName(), classMapping); |
| 111 | if (classMapping.getDeobfName() != null) { | 111 | } else { |
| 112 | classes.put(classMapping.getDeobfName(), classMapping); | 112 | classes.put(classMapping.getObfFullName(), classMapping); |
| 113 | } else { | 113 | } |
| 114 | classes.put(classMapping.getObfFullName(), classMapping); | 114 | } |
| 115 | } | 115 | |
| 116 | } | 116 | // translate the translation index |
| 117 | 117 | // NOTE: this isn't actually recursive | |
| 118 | // translate the translation index | 118 | TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); |
| 119 | // NOTE: this isn't actually recursive | 119 | |
| 120 | TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); | 120 | return new Translator(direction, classes, deobfIndex); |
| 121 | 121 | ||
| 122 | return new Translator(direction, classes, deobfIndex); | 122 | default: |
| 123 | 123 | throw new Error("Invalid translation direction!"); | |
| 124 | default: | 124 | } |
| 125 | throw new Error("Invalid translation direction!"); | 125 | } |
| 126 | } | 126 | |
| 127 | } | 127 | @Override |
| 128 | 128 | public String toString() { | |
| 129 | @Override | 129 | StringBuilder buf = new StringBuilder(); |
| 130 | public String toString() { | 130 | for (ClassMapping classMapping : this.classesByObf.values()) { |
| 131 | StringBuilder buf = new StringBuilder(); | 131 | buf.append(classMapping); |
| 132 | for (ClassMapping classMapping : this.classesByObf.values()) { | 132 | buf.append("\n"); |
| 133 | buf.append(classMapping.toString()); | 133 | } |
| 134 | buf.append("\n"); | 134 | return buf.toString(); |
| 135 | } | 135 | } |
| 136 | return buf.toString(); | 136 | |
| 137 | } | 137 | public void renameObfClass(String oldObfName, String newObfName) { |
| 138 | 138 | new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { | |
| 139 | public void renameObfClass(String oldObfName, String newObfName) { | 139 | boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; |
| 140 | new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { | 140 | assert (wasRemoved); |
| 141 | boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; | 141 | boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; |
| 142 | assert (wasRemoved); | 142 | assert (wasAdded); |
| 143 | boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; | 143 | }); |
| 144 | assert (wasAdded); | 144 | } |
| 145 | }); | 145 | |
| 146 | } | 146 | public Set<String> getAllObfClassNames() { |
| 147 | 147 | final Set<String> classNames = Sets.newHashSet(); | |
| 148 | public Set<String> getAllObfClassNames() { | 148 | for (ClassMapping classMapping : classes()) { |
| 149 | final Set<String> classNames = Sets.newHashSet(); | 149 | |
| 150 | for (ClassMapping classMapping : classes()) { | 150 | // add the class name |
| 151 | 151 | classNames.add(classMapping.getObfFullName()); | |
| 152 | // add the class name | 152 | |
| 153 | classNames.add(classMapping.getObfFullName()); | 153 | // add classes from method signatures |
| 154 | 154 | for (MethodMapping methodMapping : classMapping.methods()) { | |
| 155 | // add classes from method signatures | 155 | for (Type type : methodMapping.getObfSignature().types()) { |
| 156 | for (MethodMapping methodMapping : classMapping.methods()) { | 156 | if (type.hasClass()) { |
| 157 | for (Type type : methodMapping.getObfSignature().types()) { | 157 | classNames.add(type.getClassEntry().getClassName()); |
| 158 | if (type.hasClass()) { | 158 | } |
| 159 | classNames.add(type.getClassEntry().getClassName()); | 159 | } |
| 160 | } | 160 | } |
| 161 | } | 161 | } |
| 162 | } | 162 | return classNames; |
| 163 | } | 163 | } |
| 164 | return classNames; | 164 | |
| 165 | } | 165 | public boolean containsDeobfClass(String deobfName) { |
| 166 | 166 | return this.classesByDeobf.containsKey(deobfName); | |
| 167 | public boolean containsDeobfClass(String deobfName) { | 167 | } |
| 168 | return this.classesByDeobf.containsKey(deobfName); | 168 | |
| 169 | } | 169 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { |
| 170 | 170 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | |
| 171 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { | 171 | return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); |
| 172 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | 172 | } |
| 173 | return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); | 173 | |
| 174 | } | 174 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { |
| 175 | 175 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | |
| 176 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { | 176 | if (classMapping != null) |
| 177 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | 177 | for (FieldMapping fieldMapping : classMapping.fields()) |
| 178 | if (classMapping != null) | 178 | if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) |
| 179 | for (FieldMapping fieldMapping : classMapping.fields()) | 179 | return true; |
| 180 | if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) | 180 | |
| 181 | return true; | 181 | return false; |
| 182 | 182 | } | |
| 183 | return false; | 183 | |
| 184 | } | 184 | public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { |
| 185 | 185 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | |
| 186 | public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { | 186 | return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); |
| 187 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | 187 | } |
| 188 | return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); | 188 | |
| 189 | } | 189 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { |
| 190 | 190 | ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); | |
| 191 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { | 191 | return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); |
| 192 | ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); | 192 | } |
| 193 | return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); | 193 | |
| 194 | } | 194 | public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { |
| 195 | 195 | List<ClassMapping> mappingChain = Lists.newArrayList(); | |
| 196 | public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { | 196 | ClassMapping classMapping = null; |
| 197 | List<ClassMapping> mappingChain = Lists.newArrayList(); | 197 | for (ClassEntry obfClassEntry : obfClass.getClassChain()) { |
| 198 | ClassMapping classMapping = null; | 198 | if (mappingChain.isEmpty()) { |
| 199 | for (ClassEntry obfClassEntry : obfClass.getClassChain()) { | 199 | classMapping = this.classesByObf.get(obfClassEntry.getName()); |
| 200 | if (mappingChain.isEmpty()) { | 200 | } else if (classMapping != null) { |
| 201 | classMapping = this.classesByObf.get(obfClassEntry.getName()); | 201 | classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); |
| 202 | } else if (classMapping != null) { | 202 | } |
| 203 | classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); | 203 | mappingChain.add(classMapping); |
| 204 | } | 204 | } |
| 205 | mappingChain.add(classMapping); | 205 | return mappingChain; |
| 206 | } | 206 | } |
| 207 | return mappingChain; | 207 | |
| 208 | } | 208 | public FormatType getOriginMappingFormat() { |
| 209 | 209 | return originMapping; | |
| 210 | public FormatType getOriginMappingFormat() | 210 | } |
| 211 | { | 211 | |
| 212 | return originMapping; | 212 | public void savePreviousState() { |
| 213 | } | 213 | this.previousState = new Mappings(this.originMapping); |
| 214 | 214 | this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); | |
| 215 | public void savePreviousState() | 215 | this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); |
| 216 | { | 216 | classesByDeobf.values().forEach(ClassMapping::resetDirty); |
| 217 | this.previousState = new Mappings(this.originMapping); | 217 | classesByObf.values().forEach(ClassMapping::resetDirty); |
| 218 | this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); | 218 | } |
| 219 | this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); | 219 | |
| 220 | classesByDeobf.values().forEach(ClassMapping::resetDirty); | 220 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { |
| 221 | classesByObf.values().forEach(ClassMapping::resetDirty); | 221 | new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); |
| 222 | } | 222 | this.savePreviousState(); |
| 223 | 223 | } | |
| 224 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException | 224 | |
| 225 | { | 225 | public void saveSRGMappings(File file) throws IOException { |
| 226 | new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); | 226 | new MappingsSRGWriter().write(file, this); |
| 227 | this.savePreviousState(); | 227 | } |
| 228 | } | 228 | |
| 229 | 229 | public Mappings getPreviousState() { | |
| 230 | public void saveSRGMappings(File file) throws IOException | 230 | return previousState; |
| 231 | { | 231 | } |
| 232 | new MappingsSRGWriter().write(file, this); | 232 | |
| 233 | } | 233 | public enum FormatType { |
| 234 | 234 | ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE | |
| 235 | public Mappings getPreviousState() { | 235 | } |
| 236 | return previousState; | 236 | |
| 237 | } | 237 | public enum EntryModifier { |
| 238 | 238 | UNCHANGED, PUBLIC, PROTECTED, PRIVATE; | |
| 239 | public enum FormatType | 239 | |
| 240 | { | 240 | public String getFormattedName() { |
| 241 | ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE | 241 | return " ACC:" + super.toString(); |
| 242 | } | 242 | } |
| 243 | 243 | } | |
| 244 | public enum EntryModifier | ||
| 245 | { | ||
| 246 | UNCHANGED, PUBLIC, PROTECTED, PRIVATE; | ||
| 247 | |||
| 248 | public String getFormattedName() | ||
| 249 | { | ||
| 250 | return " ACC:" + super.toString(); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | 244 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java index 6cf279d1..172641bd 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java | |||
| @@ -8,91 +8,90 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 15 | |||
| 16 | import java.util.Map; | ||
| 17 | |||
| 18 | import cuchaz.enigma.analysis.JarIndex; | 16 | import cuchaz.enigma.analysis.JarIndex; |
| 19 | 17 | ||
| 18 | import java.util.Map; | ||
| 20 | 19 | ||
| 21 | public class MappingsChecker { | 20 | public class MappingsChecker { |
| 22 | 21 | ||
| 23 | private JarIndex index; | 22 | private JarIndex index; |
| 24 | private Map<ClassEntry, ClassMapping> droppedClassMappings; | 23 | private Map<ClassEntry, ClassMapping> droppedClassMappings; |
| 25 | private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; | 24 | private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; |
| 26 | private Map<FieldEntry, FieldMapping> droppedFieldMappings; | 25 | private Map<FieldEntry, FieldMapping> droppedFieldMappings; |
| 27 | private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; | 26 | private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; |
| 28 | 27 | ||
| 29 | public MappingsChecker(JarIndex index) { | 28 | public MappingsChecker(JarIndex index) { |
| 30 | this.index = index; | 29 | this.index = index; |
| 31 | this.droppedClassMappings = Maps.newHashMap(); | 30 | this.droppedClassMappings = Maps.newHashMap(); |
| 32 | this.droppedInnerClassMappings = Maps.newHashMap(); | 31 | this.droppedInnerClassMappings = Maps.newHashMap(); |
| 33 | this.droppedFieldMappings = Maps.newHashMap(); | 32 | this.droppedFieldMappings = Maps.newHashMap(); |
| 34 | this.droppedMethodMappings = Maps.newHashMap(); | 33 | this.droppedMethodMappings = Maps.newHashMap(); |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | public Map<ClassEntry, ClassMapping> getDroppedClassMappings() { | 36 | public Map<ClassEntry, ClassMapping> getDroppedClassMappings() { |
| 38 | return this.droppedClassMappings; | 37 | return this.droppedClassMappings; |
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() { | 40 | public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() { |
| 42 | return this.droppedInnerClassMappings; | 41 | return this.droppedInnerClassMappings; |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() { | 44 | public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() { |
| 46 | return this.droppedFieldMappings; | 45 | return this.droppedFieldMappings; |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { | 48 | public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { |
| 50 | return this.droppedMethodMappings; | 49 | return this.droppedMethodMappings; |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public void dropBrokenMappings(Mappings mappings) { | 52 | public void dropBrokenMappings(Mappings mappings) { |
| 54 | for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { | 53 | for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { |
| 55 | if (!checkClassMapping(classMapping)) { | 54 | if (!checkClassMapping(classMapping)) { |
| 56 | mappings.removeClassMapping(classMapping); | 55 | mappings.removeClassMapping(classMapping); |
| 57 | this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); | 56 | this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); |
| 58 | } | 57 | } |
| 59 | } | 58 | } |
| 60 | } | 59 | } |
| 61 | 60 | ||
| 62 | private boolean checkClassMapping(ClassMapping classMapping) { | 61 | private boolean checkClassMapping(ClassMapping classMapping) { |
| 63 | 62 | ||
| 64 | // check the class | 63 | // check the class |
| 65 | ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); | 64 | ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); |
| 66 | if (!this.index.getObfClassEntries().contains(classEntry)) { | 65 | if (!this.index.getObfClassEntries().contains(classEntry)) { |
| 67 | return false; | 66 | return false; |
| 68 | } | 67 | } |
| 69 | 68 | ||
| 70 | // check the fields | 69 | // check the fields |
| 71 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { | 70 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { |
| 72 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); | 71 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); |
| 73 | if (!this.index.containsObfField(obfFieldEntry)) { | 72 | if (!this.index.containsObfField(obfFieldEntry)) { |
| 74 | classMapping.removeFieldMapping(fieldMapping); | 73 | classMapping.removeFieldMapping(fieldMapping); |
| 75 | this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); | 74 | this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); |
| 76 | } | 75 | } |
| 77 | } | 76 | } |
| 78 | 77 | ||
| 79 | // check methods | 78 | // check methods |
| 80 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { | 79 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { |
| 81 | BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); | 80 | BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); |
| 82 | if (!this.index.containsObfBehavior(obfBehaviorEntry)) { | 81 | if (!this.index.containsObfBehavior(obfBehaviorEntry)) { |
| 83 | classMapping.removeMethodMapping(methodMapping); | 82 | classMapping.removeMethodMapping(methodMapping); |
| 84 | this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); | 83 | this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); |
| 85 | } | 84 | } |
| 86 | } | 85 | } |
| 87 | 86 | ||
| 88 | // check inner classes | 87 | // check inner classes |
| 89 | for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { | 88 | for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { |
| 90 | if (!checkClassMapping(innerClassMapping)) { | 89 | if (!checkClassMapping(innerClassMapping)) { |
| 91 | classMapping.removeInnerClassMapping(innerClassMapping); | 90 | classMapping.removeInnerClassMapping(innerClassMapping); |
| 92 | this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); | 91 | this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); |
| 93 | } | 92 | } |
| 94 | } | 93 | } |
| 95 | 94 | ||
| 96 | return true; | 95 | return true; |
| 97 | } | 96 | } |
| 98 | } | 97 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java index cdfed726..a0d43133 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java | |||
| @@ -8,178 +8,170 @@ import cuchaz.enigma.throwables.MappingParseException; | |||
| 8 | import java.io.*; | 8 | import java.io.*; |
| 9 | import java.util.Deque; | 9 | import java.util.Deque; |
| 10 | 10 | ||
| 11 | public class MappingsEnigmaReader | 11 | public class MappingsEnigmaReader { |
| 12 | { | 12 | |
| 13 | 13 | public Mappings read(File file) throws IOException, MappingParseException { | |
| 14 | public Mappings read(File file) throws IOException, MappingParseException { | 14 | Mappings mappings; |
| 15 | Mappings mappings; | 15 | |
| 16 | 16 | // Multiple file | |
| 17 | // Multiple file | 17 | if (file.isDirectory()) { |
| 18 | if (file.isDirectory()) | 18 | mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); |
| 19 | { | 19 | readDirectory(mappings, file); |
| 20 | mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); | 20 | } else { |
| 21 | readDirectory(mappings, file); | 21 | mappings = new Mappings(); |
| 22 | } | 22 | readFile(mappings, file); |
| 23 | else | 23 | } |
| 24 | { | 24 | return mappings; |
| 25 | mappings = new Mappings(); | 25 | } |
| 26 | readFile(mappings, file); | 26 | |
| 27 | } | 27 | public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { |
| 28 | return mappings; | 28 | File[] files = directory.listFiles(); |
| 29 | } | 29 | if (files != null) { |
| 30 | 30 | for (File file : files) { | |
| 31 | public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { | 31 | if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) |
| 32 | File[] files = directory.listFiles(); | 32 | readFile(mappings, file); |
| 33 | if (files != null) { | 33 | else if (file.isDirectory()) |
| 34 | for (File file : files) { | 34 | readDirectory(mappings, file.getAbsoluteFile()); |
| 35 | if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) | 35 | } |
| 36 | readFile(mappings, file); | 36 | mappings.savePreviousState(); |
| 37 | else if (file.isDirectory()) | 37 | } else |
| 38 | readDirectory(mappings, file.getAbsoluteFile()); | 38 | throw new IOException("Cannot access directory" + directory.getAbsolutePath()); |
| 39 | } | 39 | } |
| 40 | mappings.savePreviousState(); | 40 | |
| 41 | } | 41 | public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { |
| 42 | else | 42 | |
| 43 | throw new IOException("Cannot access directory" + directory.getAbsolutePath()); | 43 | BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); |
| 44 | } | 44 | Deque<Object> mappingStack = Queues.newArrayDeque(); |
| 45 | 45 | ||
| 46 | public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { | 46 | int lineNumber = 0; |
| 47 | 47 | String line; | |
| 48 | BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); | 48 | while ((line = in.readLine()) != null) { |
| 49 | Deque<Object> mappingStack = Queues.newArrayDeque(); | 49 | lineNumber++; |
| 50 | 50 | ||
| 51 | int lineNumber = 0; | 51 | // strip comments |
| 52 | String line; | 52 | int commentPos = line.indexOf('#'); |
| 53 | while ((line = in.readLine()) != null) { | 53 | if (commentPos >= 0) { |
| 54 | lineNumber++; | 54 | line = line.substring(0, commentPos); |
| 55 | 55 | } | |
| 56 | // strip comments | 56 | |
| 57 | int commentPos = line.indexOf('#'); | 57 | // skip blank lines |
| 58 | if (commentPos >= 0) { | 58 | if (line.trim().length() <= 0) { |
| 59 | line = line.substring(0, commentPos); | 59 | continue; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | // skip blank lines | 62 | // get the indent of this line |
| 63 | if (line.trim().length() <= 0) { | 63 | int indent = 0; |
| 64 | continue; | 64 | for (int i = 0; i < line.length(); i++) { |
| 65 | } | 65 | if (line.charAt(i) != '\t') { |
| 66 | 66 | break; | |
| 67 | // get the indent of this line | 67 | } |
| 68 | int indent = 0; | 68 | indent++; |
| 69 | for (int i = 0; i < line.length(); i++) { | 69 | } |
| 70 | if (line.charAt(i) != '\t') { | 70 | |
| 71 | break; | 71 | // handle stack pops |
| 72 | } | 72 | while (indent < mappingStack.size()) { |
| 73 | indent++; | 73 | mappingStack.pop(); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // handle stack pops | 76 | String[] parts = line.trim().split("\\s"); |
| 77 | while (indent < mappingStack.size()) { | 77 | try { |
| 78 | mappingStack.pop(); | 78 | // read the first token |
| 79 | } | 79 | String token = parts[0]; |
| 80 | 80 | ||
| 81 | String[] parts = line.trim().split("\\s"); | 81 | if (token.equalsIgnoreCase("CLASS")) { |
| 82 | try { | 82 | ClassMapping classMapping; |
| 83 | // read the first token | 83 | if (indent <= 0) { |
| 84 | String token = parts[0]; | 84 | // outer class |
| 85 | 85 | classMapping = readClass(parts, false); | |
| 86 | if (token.equalsIgnoreCase("CLASS")) { | 86 | mappings.addClassMapping(classMapping); |
| 87 | ClassMapping classMapping; | 87 | } else { |
| 88 | if (indent <= 0) { | 88 | |
| 89 | // outer class | 89 | // inner class |
| 90 | classMapping = readClass(parts, false); | 90 | if (!(mappingStack.peek() instanceof ClassMapping)) { |
| 91 | mappings.addClassMapping(classMapping); | 91 | throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); |
| 92 | } else { | 92 | } |
| 93 | 93 | ||
| 94 | // inner class | 94 | classMapping = readClass(parts, true); |
| 95 | if (!(mappingStack.peek() instanceof ClassMapping)) { | 95 | ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); |
| 96 | throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); | 96 | } |
| 97 | } | 97 | mappingStack.push(classMapping); |
| 98 | 98 | } else if (token.equalsIgnoreCase("FIELD")) { | |
| 99 | classMapping = readClass(parts, true); | 99 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { |
| 100 | ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); | 100 | throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); |
| 101 | } | 101 | } |
| 102 | mappingStack.push(classMapping); | 102 | ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); |
| 103 | } else if (token.equalsIgnoreCase("FIELD")) { | 103 | } else if (token.equalsIgnoreCase("METHOD")) { |
| 104 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { | 104 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { |
| 105 | throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); | 105 | throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); |
| 106 | } | 106 | } |
| 107 | ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); | 107 | MethodMapping methodMapping = readMethod(parts); |
| 108 | } else if (token.equalsIgnoreCase("METHOD")) { | 108 | ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); |
| 109 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { | 109 | mappingStack.push(methodMapping); |
| 110 | throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); | 110 | } else if (token.equalsIgnoreCase("ARG")) { |
| 111 | } | 111 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { |
| 112 | MethodMapping methodMapping = readMethod(parts); | 112 | throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!"); |
| 113 | ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); | 113 | } |
| 114 | mappingStack.push(methodMapping); | 114 | ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); |
| 115 | } else if (token.equalsIgnoreCase("ARG")) { | 115 | } |
| 116 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { | 116 | } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { |
| 117 | throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!"); | 117 | throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); |
| 118 | } | 118 | } catch (MappingConflict e) { |
| 119 | ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); | 119 | throw new MappingParseException(file, lineNumber, e.getMessage()); |
| 120 | } | 120 | } |
| 121 | } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { | 121 | } |
| 122 | throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); | 122 | in.close(); |
| 123 | } catch (MappingConflict e) { | 123 | return mappings; |
| 124 | throw new MappingParseException(file, lineNumber, e.getMessage()); | 124 | } |
| 125 | } | 125 | |
| 126 | } | 126 | private ArgumentMapping readArgument(String[] parts) { |
| 127 | in.close(); | 127 | return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); |
| 128 | return mappings; | 128 | } |
| 129 | } | 129 | |
| 130 | 130 | private ClassMapping readClass(String[] parts, boolean makeSimple) { | |
| 131 | private ArgumentMapping readArgument(String[] parts) { | 131 | if (parts.length == 2) { |
| 132 | return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); | 132 | return new ClassMapping(parts[1]); |
| 133 | } | 133 | } else if (parts.length == 3) { |
| 134 | 134 | boolean access = parts[2].startsWith("ACC:"); | |
| 135 | private ClassMapping readClass(String[] parts, boolean makeSimple) { | 135 | ClassMapping mapping; |
| 136 | if (parts.length == 2) { | 136 | if (access) |
| 137 | return new ClassMapping(parts[1]); | 137 | mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); |
| 138 | } else if (parts.length == 3) { | 138 | else |
| 139 | boolean access = parts[2].startsWith("ACC:"); | 139 | mapping = new ClassMapping(parts[1], parts[2]); |
| 140 | ClassMapping mapping; | 140 | |
| 141 | if (access) | 141 | return mapping; |
| 142 | mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); | 142 | } else if (parts.length == 4) |
| 143 | else | 143 | return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); |
| 144 | mapping = new ClassMapping(parts[1], parts[2]); | 144 | return null; |
| 145 | 145 | } | |
| 146 | return mapping; | 146 | |
| 147 | } else if (parts.length == 4) | 147 | /* TEMP */ |
| 148 | return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); | 148 | protected FieldMapping readField(String[] parts) { |
| 149 | return null; | 149 | FieldMapping mapping = null; |
| 150 | } | 150 | if (parts.length == 4) { |
| 151 | 151 | boolean access = parts[3].startsWith("ACC:"); | |
| 152 | /* TEMP */ | 152 | if (access) |
| 153 | protected FieldMapping readField(String[] parts) { | 153 | mapping = new FieldMapping(parts[1], new Type(parts[2]), null, |
| 154 | FieldMapping mapping = null; | 154 | Mappings.EntryModifier.valueOf(parts[3].substring(4))); |
| 155 | if (parts.length == 4) | 155 | else |
| 156 | { | 156 | mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); |
| 157 | boolean access = parts[3].startsWith("ACC:"); | 157 | } else if (parts.length == 5) |
| 158 | if (access) | 158 | mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); |
| 159 | mapping = new FieldMapping(parts[1], new Type(parts[2]), null, | 159 | return mapping; |
| 160 | Mappings.EntryModifier.valueOf(parts[3].substring(4))); | 160 | } |
| 161 | else | 161 | |
| 162 | mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); | 162 | private MethodMapping readMethod(String[] parts) { |
| 163 | } | 163 | MethodMapping mapping = null; |
| 164 | else if (parts.length == 5) | 164 | if (parts.length == 3) |
| 165 | mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); | 165 | mapping = new MethodMapping(parts[1], new Signature(parts[2])); |
| 166 | return mapping; | 166 | else if (parts.length == 4) { |
| 167 | } | 167 | boolean access = parts[3].startsWith("ACC:"); |
| 168 | 168 | if (access) | |
| 169 | private MethodMapping readMethod(String[] parts) { | 169 | mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); |
| 170 | MethodMapping mapping = null; | 170 | else |
| 171 | if (parts.length == 3) | 171 | mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); |
| 172 | mapping = new MethodMapping(parts[1], new Signature(parts[2])); | 172 | } else if (parts.length == 5) |
| 173 | else if (parts.length == 4){ | 173 | mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], |
| 174 | boolean access = parts[3].startsWith("ACC:"); | 174 | Mappings.EntryModifier.valueOf(parts[4].substring(4))); |
| 175 | if (access) | 175 | return mapping; |
| 176 | mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); | 176 | } |
| 177 | else | ||
| 178 | mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); | ||
| 179 | } | ||
| 180 | else if (parts.length == 5) | ||
| 181 | mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], | ||
| 182 | Mappings.EntryModifier.valueOf(parts[4].substring(4))); | ||
| 183 | return mapping; | ||
| 184 | } | ||
| 185 | } | 177 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java index 6c57200f..ba1b258b 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.base.Charsets; | 14 | import com.google.common.base.Charsets; |
| @@ -18,15 +19,13 @@ import java.util.Collections; | |||
| 18 | import java.util.List; | 19 | import java.util.List; |
| 19 | 20 | ||
| 20 | public class MappingsEnigmaWriter { | 21 | public class MappingsEnigmaWriter { |
| 21 | 22 | ||
| 22 | public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException { | 23 | public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException { |
| 23 | if (!isDirectoryFormat) | 24 | if (!isDirectoryFormat) { |
| 24 | { | ||
| 25 | PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8)); | 25 | PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8)); |
| 26 | write(outputWriter, mappings); | 26 | write(outputWriter, mappings); |
| 27 | outputWriter.close(); | 27 | outputWriter.close(); |
| 28 | } | 28 | } else |
| 29 | else | ||
| 30 | writeAsDirectory(out, mappings); | 29 | writeAsDirectory(out, mappings); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| @@ -42,8 +41,7 @@ public class MappingsEnigmaWriter { | |||
| 42 | File result; | 41 | File result; |
| 43 | if (classMapping.getDeobfName() == null) | 42 | if (classMapping.getDeobfName() == null) |
| 44 | result = obFile; | 43 | result = obFile; |
| 45 | else | 44 | else { |
| 46 | { | ||
| 47 | // Make sure that old version of the file doesn't exist | 45 | // Make sure that old version of the file doesn't exist |
| 48 | if (obFile.exists()) | 46 | if (obFile.exists()) |
| 49 | obFile.delete(); | 47 | obFile.delete(); |
| @@ -59,19 +57,16 @@ public class MappingsEnigmaWriter { | |||
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | // Remove dropped mappings | 59 | // Remove dropped mappings |
| 62 | if (mappings.getPreviousState() != null) | 60 | if (mappings.getPreviousState() != null) { |
| 63 | { | ||
| 64 | List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); | 61 | List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); |
| 65 | List<ClassMapping> classMappings = new ArrayList<>(mappings.classes()); | 62 | List<ClassMapping> classMappings = new ArrayList<>(mappings.classes()); |
| 66 | droppedClassMappings.removeAll(classMappings); | 63 | droppedClassMappings.removeAll(classMappings); |
| 67 | for (ClassMapping classMapping : droppedClassMappings) | 64 | for (ClassMapping classMapping : droppedClassMappings) { |
| 68 | { | ||
| 69 | File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); | 65 | File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); |
| 70 | File result; | 66 | File result; |
| 71 | if (classMapping.getDeobfName() == null) | 67 | if (classMapping.getDeobfName() == null) |
| 72 | result = obFile; | 68 | result = obFile; |
| 73 | else | 69 | else { |
| 74 | { | ||
| 75 | // Make sure that old version of the file doesn't exist | 70 | // Make sure that old version of the file doesn't exist |
| 76 | if (obFile.exists()) | 71 | if (obFile.exists()) |
| 77 | obFile.delete(); | 72 | obFile.delete(); |
| @@ -86,18 +81,15 @@ public class MappingsEnigmaWriter { | |||
| 86 | private void deletePreviousClassMapping(File target, ClassMapping classMapping) { | 81 | private void deletePreviousClassMapping(File target, ClassMapping classMapping) { |
| 87 | File prevFile = null; | 82 | File prevFile = null; |
| 88 | // Deob rename | 83 | // Deob rename |
| 89 | if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) | 84 | if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) { |
| 90 | { | ||
| 91 | prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); | 85 | prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); |
| 92 | } | 86 | } |
| 93 | // Deob to ob rename | 87 | // Deob to ob rename |
| 94 | else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) | 88 | else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) { |
| 95 | { | ||
| 96 | prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); | 89 | prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); |
| 97 | } | 90 | } |
| 98 | // Ob to Deob rename | 91 | // Ob to Deob rename |
| 99 | else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) | 92 | else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) { |
| 100 | { | ||
| 101 | prevFile = new File(target, classMapping.getObfFullName() + ".mapping"); | 93 | prevFile = new File(target, classMapping.getObfFullName() + ".mapping"); |
| 102 | } | 94 | } |
| 103 | 95 | ||
| @@ -110,50 +102,56 @@ public class MappingsEnigmaWriter { | |||
| 110 | write(out, classMapping, 0); | 102 | write(out, classMapping, 0); |
| 111 | } | 103 | } |
| 112 | } | 104 | } |
| 113 | 105 | ||
| 114 | private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { | 106 | private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { |
| 115 | if (classMapping.getDeobfName() == null) { | 107 | if (classMapping.getDeobfName() == null) { |
| 116 | out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | 108 | out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), |
| 109 | classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | ||
| 117 | } else { | 110 | } else { |
| 118 | out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | 111 | out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), |
| 112 | classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | ||
| 119 | } | 113 | } |
| 120 | 114 | ||
| 121 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { | 115 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { |
| 122 | write(out, innerClassMapping, depth + 1); | 116 | write(out, innerClassMapping, depth + 1); |
| 123 | } | 117 | } |
| 124 | 118 | ||
| 125 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { | 119 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { |
| 126 | write(out, fieldMapping, depth + 1); | 120 | write(out, fieldMapping, depth + 1); |
| 127 | } | 121 | } |
| 128 | 122 | ||
| 129 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { | 123 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { |
| 130 | write(out, methodMapping, depth + 1); | 124 | write(out, methodMapping, depth + 1); |
| 131 | } | 125 | } |
| 132 | } | 126 | } |
| 133 | 127 | ||
| 134 | private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { | 128 | private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { |
| 135 | if (fieldMapping.getDeobfName() == null) | 129 | if (fieldMapping.getDeobfName() == null) |
| 136 | out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | 130 | out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), |
| 131 | fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | ||
| 137 | else | 132 | else |
| 138 | out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | 133 | out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), |
| 134 | fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | ||
| 139 | } | 135 | } |
| 140 | 136 | ||
| 141 | private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { | 137 | private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { |
| 142 | if (methodMapping.getDeobfName() == null) { | 138 | if (methodMapping.getDeobfName() == null) { |
| 143 | out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" :methodMapping.getModifier().getFormattedName()); | 139 | out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), |
| 140 | methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); | ||
| 144 | } else { | 141 | } else { |
| 145 | out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); | 142 | out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), |
| 143 | methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); | ||
| 146 | } | 144 | } |
| 147 | 145 | ||
| 148 | for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { | 146 | for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { |
| 149 | write(out, argumentMapping, depth + 1); | 147 | write(out, argumentMapping, depth + 1); |
| 150 | } | 148 | } |
| 151 | } | 149 | } |
| 152 | 150 | ||
| 153 | private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { | 151 | private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { |
| 154 | out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); | 152 | out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); |
| 155 | } | 153 | } |
| 156 | 154 | ||
| 157 | private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { | 155 | private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { |
| 158 | List<T> out = new ArrayList<>(); | 156 | List<T> out = new ArrayList<>(); |
| 159 | for (T t : classes) { | 157 | for (T t : classes) { |
| @@ -162,7 +160,7 @@ public class MappingsEnigmaWriter { | |||
| 162 | Collections.sort(out); | 160 | Collections.sort(out); |
| 163 | return out; | 161 | return out; |
| 164 | } | 162 | } |
| 165 | 163 | ||
| 166 | private String getIndent(int depth) { | 164 | private String getIndent(int depth) { |
| 167 | StringBuilder buf = new StringBuilder(); | 165 | StringBuilder buf = new StringBuilder(); |
| 168 | for (int i = 0; i < depth; i++) { | 166 | for (int i = 0; i < depth; i++) { |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java index e1428ea0..7126d2b6 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -8,8 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.JarIndex; | ||
| 16 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 17 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 18 | |||
| 13 | import java.io.IOException; | 19 | import java.io.IOException; |
| 14 | import java.io.ObjectOutputStream; | 20 | import java.io.ObjectOutputStream; |
| 15 | import java.io.OutputStream; | 21 | import java.io.OutputStream; |
| @@ -17,324 +23,315 @@ import java.util.List; | |||
| 17 | import java.util.Set; | 23 | import java.util.Set; |
| 18 | import java.util.zip.GZIPOutputStream; | 24 | import java.util.zip.GZIPOutputStream; |
| 19 | 25 | ||
| 20 | import com.google.common.collect.Lists; | ||
| 21 | import cuchaz.enigma.analysis.JarIndex; | ||
| 22 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 23 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 24 | |||
| 25 | public class MappingsRenamer { | 26 | public class MappingsRenamer { |
| 26 | 27 | ||
| 27 | private JarIndex index; | 28 | private JarIndex index; |
| 28 | private Mappings mappings; | 29 | private Mappings mappings; |
| 29 | 30 | ||
| 30 | public MappingsRenamer(JarIndex index, Mappings mappings) { | 31 | public MappingsRenamer(JarIndex index, Mappings mappings) { |
| 31 | this.index = index; | 32 | this.index = index; |
| 32 | this.mappings = mappings; | 33 | this.mappings = mappings; |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | public void setMappings(Mappings mappings) | 36 | public void setMappings(Mappings mappings) { |
| 36 | { | 37 | this.mappings = mappings; |
| 37 | this.mappings = mappings; | 38 | } |
| 38 | } | 39 | |
| 39 | 40 | public void setClassName(ClassEntry obf, String deobfName) { | |
| 40 | public void setClassName(ClassEntry obf, String deobfName) { | 41 | |
| 41 | 42 | deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); | |
| 42 | deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); | 43 | |
| 43 | 44 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | |
| 44 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | 45 | if (mappingChain.size() == 1) { |
| 45 | if (mappingChain.size() == 1) { | 46 | |
| 46 | 47 | if (deobfName != null) { | |
| 47 | if (deobfName != null) { | 48 | // make sure we don't rename to an existing obf or deobf class |
| 48 | // make sure we don't rename to an existing obf or deobf class | 49 | if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { |
| 49 | if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { | 50 | throw new IllegalNameException(deobfName, "There is already a class with that name"); |
| 50 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | 51 | } |
| 51 | } | 52 | } |
| 52 | } | 53 | |
| 53 | 54 | ClassMapping classMapping = mappingChain.get(0); | |
| 54 | ClassMapping classMapping = mappingChain.get(0); | 55 | mappings.setClassDeobfName(classMapping, deobfName); |
| 55 | mappings.setClassDeobfName(classMapping, deobfName); | 56 | |
| 56 | 57 | } else { | |
| 57 | } else { | 58 | |
| 58 | 59 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | |
| 59 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | 60 | |
| 60 | 61 | if (deobfName != null) { | |
| 61 | if (deobfName != null) { | 62 | // make sure we don't rename to an existing obf or deobf inner class |
| 62 | // make sure we don't rename to an existing obf or deobf inner class | 63 | if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { |
| 63 | if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { | 64 | throw new IllegalNameException(deobfName, "There is already a class with that name"); |
| 64 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | 65 | } |
| 65 | } | 66 | } |
| 66 | } | 67 | |
| 67 | 68 | outerClassMapping.setInnerClassName(obf, deobfName); | |
| 68 | outerClassMapping.setInnerClassName(obf, deobfName); | 69 | } |
| 69 | } | 70 | } |
| 70 | } | 71 | |
| 71 | 72 | public void removeClassMapping(ClassEntry obf) { | |
| 72 | public void removeClassMapping(ClassEntry obf) { | 73 | setClassName(obf, null); |
| 73 | setClassName(obf, null); | 74 | } |
| 74 | } | 75 | |
| 75 | 76 | public void markClassAsDeobfuscated(ClassEntry obf) { | |
| 76 | public void markClassAsDeobfuscated(ClassEntry obf) { | 77 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); |
| 77 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); | 78 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); |
| 78 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | 79 | if (mappingChain.size() == 1) { |
| 79 | if (mappingChain.size() == 1) { | 80 | ClassMapping classMapping = mappingChain.get(0); |
| 80 | ClassMapping classMapping = mappingChain.get(0); | 81 | mappings.setClassDeobfName(classMapping, deobfName); |
| 81 | mappings.setClassDeobfName(classMapping, deobfName); | 82 | } else { |
| 82 | } else { | 83 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); |
| 83 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | 84 | outerClassMapping.setInnerClassName(obf, deobfName); |
| 84 | outerClassMapping.setInnerClassName(obf, deobfName); | 85 | } |
| 85 | } | 86 | } |
| 86 | } | 87 | |
| 87 | 88 | public void setFieldName(FieldEntry obf, String deobfName) { | |
| 88 | public void setFieldName(FieldEntry obf, String deobfName) { | 89 | deobfName = NameValidator.validateFieldName(deobfName); |
| 89 | deobfName = NameValidator.validateFieldName(deobfName); | 90 | FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); |
| 90 | FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); | 91 | ClassEntry definedClass = null; |
| 91 | ClassEntry definedClass = null; | 92 | if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) |
| 92 | if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) | 93 | definedClass = obf.getClassEntry(); |
| 93 | definedClass = obf.getClassEntry(); | 94 | else { |
| 94 | else { | 95 | for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { |
| 95 | for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { | 96 | if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { |
| 96 | if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { | 97 | definedClass = ancestorEntry; |
| 97 | definedClass = ancestorEntry; | 98 | break; |
| 98 | break; | 99 | } |
| 99 | } | 100 | } |
| 100 | } | 101 | } |
| 101 | } | 102 | |
| 102 | 103 | if (definedClass != null) { | |
| 103 | if (definedClass != null) { | 104 | String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); |
| 104 | String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); | 105 | if (className == null) |
| 105 | if (className == null) | 106 | className = definedClass.getClassName(); |
| 106 | className = definedClass.getClassName(); | 107 | throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); |
| 107 | throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); | 108 | } |
| 108 | } | 109 | |
| 109 | 110 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | |
| 110 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 111 | classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); |
| 111 | classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); | 112 | } |
| 112 | } | 113 | |
| 113 | 114 | public void removeFieldMapping(FieldEntry obf) { | |
| 114 | public void removeFieldMapping(FieldEntry obf) { | 115 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 115 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 116 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); |
| 116 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); | 117 | } |
| 117 | } | 118 | |
| 118 | 119 | public void markFieldAsDeobfuscated(FieldEntry obf) { | |
| 119 | public void markFieldAsDeobfuscated(FieldEntry obf) { | 120 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 120 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 121 | classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); |
| 121 | classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); | 122 | } |
| 122 | } | 123 | |
| 123 | 124 | private void validateMethodTreeName(MethodEntry entry, String deobfName) { | |
| 124 | private void validateMethodTreeName(MethodEntry entry, String deobfName) { | 125 | MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); |
| 125 | MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); | 126 | |
| 126 | 127 | // TODO: Verify if I don't break things | |
| 127 | // TODO: Verify if I don't break things | 128 | ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); |
| 128 | ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); | 129 | if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) |
| 129 | if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) | 130 | || index.containsObfBehavior(targetEntry)) { |
| 130 | || index.containsObfBehavior(targetEntry)) { | 131 | String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); |
| 131 | String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); | 132 | if (deobfClassName == null) { |
| 132 | if (deobfClassName == null) { | 133 | deobfClassName = entry.getClassName(); |
| 133 | deobfClassName = entry.getClassName(); | 134 | } |
| 134 | } | 135 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); |
| 135 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | 136 | } |
| 136 | } | 137 | |
| 137 | 138 | for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { | |
| 138 | for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { | 139 | validateMethodTreeName(entry.cloneToNewClass(child), deobfName); |
| 139 | validateMethodTreeName(entry.cloneToNewClass(child), deobfName); | 140 | } |
| 140 | } | 141 | } |
| 141 | } | 142 | |
| 142 | 143 | public void setMethodTreeName(MethodEntry obf, String deobfName) { | |
| 143 | public void setMethodTreeName(MethodEntry obf, String deobfName) { | 144 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf); |
| 144 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf); | 145 | |
| 145 | 146 | deobfName = NameValidator.validateMethodName(deobfName); | |
| 146 | deobfName = NameValidator.validateMethodName(deobfName); | 147 | for (MethodEntry entry : implementations) { |
| 147 | for (MethodEntry entry : implementations) { | 148 | validateMethodTreeName(entry, deobfName); |
| 148 | validateMethodTreeName(entry, deobfName); | 149 | } |
| 149 | } | 150 | |
| 150 | 151 | for (MethodEntry entry : implementations) { | |
| 151 | for (MethodEntry entry : implementations) { | 152 | setMethodName(entry, deobfName); |
| 152 | setMethodName(entry, deobfName); | 153 | } |
| 153 | } | 154 | } |
| 154 | } | 155 | |
| 155 | 156 | public void setMethodName(MethodEntry obf, String deobfName) { | |
| 156 | public void setMethodName(MethodEntry obf, String deobfName) { | 157 | deobfName = NameValidator.validateMethodName(deobfName); |
| 157 | deobfName = NameValidator.validateMethodName(deobfName); | 158 | MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); |
| 158 | MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); | 159 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 159 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 160 | |
| 160 | 161 | // TODO: Verify if I don't break things | |
| 161 | // TODO: Verify if I don't break things | 162 | if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) |
| 162 | if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) | 163 | || index.containsObfBehavior(targetEntry)) { |
| 163 | || index.containsObfBehavior(targetEntry)) { | 164 | String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); |
| 164 | String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); | 165 | if (deobfClassName == null) { |
| 165 | if (deobfClassName == null) { | 166 | deobfClassName = obf.getClassName(); |
| 166 | deobfClassName = obf.getClassName(); | 167 | } |
| 167 | } | 168 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); |
| 168 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | 169 | } |
| 169 | } | 170 | |
| 170 | 171 | classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); | |
| 171 | classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); | 172 | } |
| 172 | } | 173 | |
| 173 | 174 | public void removeMethodTreeMapping(MethodEntry obf) { | |
| 174 | public void removeMethodTreeMapping(MethodEntry obf) { | 175 | index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); |
| 175 | index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); | 176 | } |
| 176 | } | 177 | |
| 177 | 178 | public void removeMethodMapping(MethodEntry obf) { | |
| 178 | public void removeMethodMapping(MethodEntry obf) { | 179 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 179 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 180 | classMapping.setMethodName(obf.getName(), obf.getSignature(), null); |
| 180 | classMapping.setMethodName(obf.getName(), obf.getSignature(), null); | 181 | } |
| 181 | } | 182 | |
| 182 | 183 | public void markMethodTreeAsDeobfuscated(MethodEntry obf) { | |
| 183 | public void markMethodTreeAsDeobfuscated(MethodEntry obf) { | 184 | index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); |
| 184 | index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); | 185 | } |
| 185 | } | 186 | |
| 186 | 187 | public void markMethodAsDeobfuscated(MethodEntry obf) { | |
| 187 | public void markMethodAsDeobfuscated(MethodEntry obf) { | 188 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 188 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 189 | classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); |
| 189 | classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); | 190 | } |
| 190 | } | 191 | |
| 191 | 192 | public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { | |
| 192 | public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { | 193 | if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { |
| 193 | if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { | 194 | setArgumentName(obf, deobfName); |
| 194 | setArgumentName(obf, deobfName); | 195 | return; |
| 195 | return; | 196 | } |
| 196 | } | 197 | |
| 197 | 198 | MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); | |
| 198 | MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); | 199 | |
| 199 | 200 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); | |
| 200 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); | 201 | for (MethodEntry entry : implementations) { |
| 201 | for (MethodEntry entry : implementations) { | 202 | ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); |
| 202 | ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); | 203 | if (classMapping != null) { |
| 203 | if (classMapping != null) { | 204 | MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); |
| 204 | MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); | 205 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon |
| 205 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon | 206 | // TODO: Verify if I don't break things |
| 206 | // TODO: Verify if I don't break things | 207 | if (mapping != null) { |
| 207 | if (mapping != null) { | 208 | for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { |
| 208 | for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { | 209 | if (argumentMapping.getIndex() != obf.getIndex()) { |
| 209 | if (argumentMapping.getIndex() != obf.getIndex()) { | 210 | if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) |
| 210 | if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) | 211 | || argumentMapping.getName().equals(deobfName)) { |
| 211 | || argumentMapping.getName().equals(deobfName)) { | 212 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); |
| 212 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); | 213 | } |
| 213 | } | 214 | } |
| 214 | } | 215 | } |
| 215 | } | 216 | } |
| 216 | } | 217 | } |
| 217 | } | 218 | } |
| 218 | } | 219 | |
| 219 | 220 | for (MethodEntry entry : implementations) { | |
| 220 | for (MethodEntry entry : implementations) { | 221 | setArgumentName(new ArgumentEntry(obf, entry), deobfName); |
| 221 | setArgumentName(new ArgumentEntry(obf, entry), deobfName); | 222 | } |
| 222 | } | 223 | } |
| 223 | } | 224 | |
| 224 | 225 | public void setArgumentName(ArgumentEntry obf, String deobfName) { | |
| 225 | public void setArgumentName(ArgumentEntry obf, String deobfName) { | 226 | deobfName = NameValidator.validateArgumentName(deobfName); |
| 226 | deobfName = NameValidator.validateArgumentName(deobfName); | 227 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 227 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 228 | MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); |
| 228 | MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); | 229 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon |
| 229 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon | 230 | // TODO: Verify if I don't break things |
| 230 | // TODO: Verify if I don't break things | 231 | if (mapping != null) { |
| 231 | if (mapping != null) { | 232 | for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { |
| 232 | for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { | 233 | if (argumentMapping.getIndex() != obf.getIndex()) { |
| 233 | if (argumentMapping.getIndex() != obf.getIndex()) { | 234 | if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) |
| 234 | if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) | 235 | || argumentMapping.getName().equals(deobfName)) { |
| 235 | || argumentMapping.getName().equals(deobfName)) { | 236 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); |
| 236 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); | 237 | } |
| 237 | } | 238 | } |
| 238 | } | 239 | } |
| 239 | } | 240 | } |
| 240 | } | 241 | |
| 241 | 242 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); | |
| 242 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); | 243 | } |
| 243 | } | 244 | |
| 244 | 245 | public void removeArgumentMapping(ArgumentEntry obf) { | |
| 245 | public void removeArgumentMapping(ArgumentEntry obf) { | 246 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 246 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 247 | classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); |
| 247 | classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); | 248 | } |
| 248 | } | 249 | |
| 249 | 250 | public void markArgumentAsDeobfuscated(ArgumentEntry obf) { | |
| 250 | public void markArgumentAsDeobfuscated(ArgumentEntry obf) { | 251 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); |
| 251 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | 252 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); |
| 252 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); | 253 | } |
| 253 | } | 254 | |
| 254 | 255 | public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { | |
| 255 | public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { | 256 | classMapping.removeFieldMapping(fieldMapping); |
| 256 | classMapping.removeFieldMapping(fieldMapping); | 257 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); |
| 257 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | 258 | if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { |
| 258 | if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { | 259 | if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { |
| 259 | if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { | 260 | targetClassMapping.addFieldMapping(fieldMapping); |
| 260 | targetClassMapping.addFieldMapping(fieldMapping); | 261 | return true; |
| 261 | return true; | 262 | } else { |
| 262 | } else { | 263 | System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); |
| 263 | System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); | 264 | } |
| 264 | } | 265 | } |
| 265 | } | 266 | return false; |
| 266 | return false; | 267 | } |
| 267 | } | 268 | |
| 268 | 269 | public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { | |
| 269 | public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { | 270 | classMapping.removeMethodMapping(methodMapping); |
| 270 | classMapping.removeMethodMapping(methodMapping); | 271 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); |
| 271 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | 272 | if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { |
| 272 | if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { | 273 | if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { |
| 273 | if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { | 274 | targetClassMapping.addMethodMapping(methodMapping); |
| 274 | targetClassMapping.addMethodMapping(methodMapping); | 275 | return true; |
| 275 | return true; | 276 | } else { |
| 276 | } else { | 277 | System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); |
| 277 | System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); | 278 | } |
| 278 | } | 279 | } |
| 279 | } | 280 | return false; |
| 280 | return false; | 281 | } |
| 281 | } | 282 | |
| 282 | 283 | public void write(OutputStream out) throws IOException { | |
| 283 | public void write(OutputStream out) throws IOException { | 284 | // TEMP: just use the object output for now. We can find a more efficient storage format later |
| 284 | // TEMP: just use the object output for now. We can find a more efficient storage format later | 285 | GZIPOutputStream gzipout = new GZIPOutputStream(out); |
| 285 | GZIPOutputStream gzipout = new GZIPOutputStream(out); | 286 | ObjectOutputStream oout = new ObjectOutputStream(gzipout); |
| 286 | ObjectOutputStream oout = new ObjectOutputStream(gzipout); | 287 | oout.writeObject(this); |
| 287 | oout.writeObject(this); | 288 | gzipout.finish(); |
| 288 | gzipout.finish(); | 289 | } |
| 289 | } | 290 | |
| 290 | 291 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { | |
| 291 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { | 292 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); |
| 292 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); | 293 | return mappingChain.get(mappingChain.size() - 1); |
| 293 | return mappingChain.get(mappingChain.size() - 1); | 294 | } |
| 294 | } | 295 | |
| 295 | 296 | private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) { | |
| 296 | private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) { | 297 | List<ClassEntry> classChain = obfClassEntry.getClassChain(); |
| 297 | List<ClassEntry> classChain = obfClassEntry.getClassChain(); | 298 | List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry); |
| 298 | List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry); | 299 | for (int i = 0; i < classChain.size(); i++) { |
| 299 | for (int i = 0; i < classChain.size(); i++) { | 300 | ClassEntry classEntry = classChain.get(i); |
| 300 | ClassEntry classEntry = classChain.get(i); | 301 | ClassMapping classMapping = mappingChain.get(i); |
| 301 | ClassMapping classMapping = mappingChain.get(i); | 302 | if (classMapping == null) { |
| 302 | if (classMapping == null) { | 303 | |
| 303 | 304 | // create it | |
| 304 | // create it | 305 | classMapping = new ClassMapping(classEntry.getName()); |
| 305 | classMapping = new ClassMapping(classEntry.getName()); | 306 | mappingChain.set(i, classMapping); |
| 306 | mappingChain.set(i, classMapping); | 307 | |
| 307 | 308 | // add it to the right parent | |
| 308 | // add it to the right parent | 309 | try { |
| 309 | try { | 310 | if (i == 0) { |
| 310 | if (i == 0) { | 311 | mappings.addClassMapping(classMapping); |
| 311 | mappings.addClassMapping(classMapping); | 312 | } else { |
| 312 | } else { | 313 | mappingChain.get(i - 1).addInnerClassMapping(classMapping); |
| 313 | mappingChain.get(i - 1).addInnerClassMapping(classMapping); | 314 | } |
| 314 | } | 315 | } catch (MappingConflict mappingConflict) { |
| 315 | } catch (MappingConflict mappingConflict) { | 316 | mappingConflict.printStackTrace(); |
| 316 | mappingConflict.printStackTrace(); | 317 | } |
| 317 | } | 318 | } |
| 318 | } | 319 | } |
| 319 | } | 320 | return mappingChain; |
| 320 | return mappingChain; | 321 | } |
| 321 | } | 322 | |
| 322 | 323 | public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) { | |
| 323 | public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) | 324 | ClassMapping classMapping = getOrCreateClassMapping(obEntry); |
| 324 | { | 325 | classMapping.setModifier(modifier); |
| 325 | ClassMapping classMapping = getOrCreateClassMapping(obEntry); | 326 | } |
| 326 | classMapping.setModifier(modifier); | 327 | |
| 327 | } | 328 | public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { |
| 328 | 329 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); | |
| 329 | public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) | 330 | classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); |
| 330 | { | 331 | } |
| 331 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); | 332 | |
| 332 | classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); | 333 | public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { |
| 333 | } | 334 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); |
| 334 | 335 | classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); | |
| 335 | public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) | 336 | } |
| 336 | { | ||
| 337 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); | ||
| 338 | classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); | ||
| 339 | } | ||
| 340 | } | 337 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java index a3f0cc87..b0eb826e 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java | |||
| @@ -13,69 +13,67 @@ import java.util.List; | |||
| 13 | */ | 13 | */ |
| 14 | public class MappingsSRGWriter { | 14 | public class MappingsSRGWriter { |
| 15 | 15 | ||
| 16 | public void write(File file, Mappings mappings) throws IOException { | 16 | public void write(File file, Mappings mappings) throws IOException { |
| 17 | if(file.exists()){ | 17 | if (file.exists()) { |
| 18 | file.delete(); | 18 | file.delete(); |
| 19 | } | 19 | } |
| 20 | file.createNewFile(); | 20 | file.createNewFile(); |
| 21 | 21 | ||
| 22 | TranslationIndex index = new TranslationIndex(); | 22 | TranslationIndex index = new TranslationIndex(); |
| 23 | 23 | ||
| 24 | PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); | 24 | PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); |
| 25 | List<String> fieldMappings = new ArrayList<>(); | 25 | List<String> fieldMappings = new ArrayList<>(); |
| 26 | List<String> methodMappings = new ArrayList<>(); | 26 | List<String> methodMappings = new ArrayList<>(); |
| 27 | for (ClassMapping classMapping : sorted(mappings.classes())) { | 27 | for (ClassMapping classMapping : sorted(mappings.classes())) { |
| 28 | if(classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null){ | 28 | if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) { |
| 29 | continue; | 29 | continue; |
| 30 | } | 30 | } |
| 31 | writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); | 31 | writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); |
| 32 | writer.write(System.lineSeparator()); | 32 | writer.write(System.lineSeparator()); |
| 33 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { | 33 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { |
| 34 | if(innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null){ | 34 | if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) { |
| 35 | continue; | 35 | continue; |
| 36 | } | 36 | } |
| 37 | String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); | 37 | String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); |
| 38 | String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); | 38 | String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); |
| 39 | writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); | 39 | writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); |
| 40 | writer.write(System.lineSeparator()); | 40 | writer.write(System.lineSeparator()); |
| 41 | for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { | 41 | for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { |
| 42 | fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); | 42 | fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { | 45 | for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { |
| 46 | methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); | 46 | methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); |
| 47 | } | 47 | } |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { | 50 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { |
| 51 | fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); | 51 | fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { | 54 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { |
| 55 | methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); | 55 | methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); |
| 56 | } | 56 | } |
| 57 | } | 57 | } |
| 58 | for(String fd : fieldMappings){ | 58 | for (String fd : fieldMappings) { |
| 59 | writer.write(fd); | 59 | writer.write(fd); |
| 60 | writer.write(System.lineSeparator()); | 60 | writer.write(System.lineSeparator()); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | for(String md : methodMappings){ | 63 | for (String md : methodMappings) { |
| 64 | writer.write(md); | 64 | writer.write(md); |
| 65 | writer.write(System.lineSeparator()); | 65 | writer.write(System.lineSeparator()); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | writer.close(); | ||
| 69 | } | ||
| 68 | 70 | ||
| 69 | writer.close(); | 71 | private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { |
| 70 | } | 72 | List<T> out = new ArrayList<>(); |
| 71 | 73 | for (T t : classes) { | |
| 72 | 74 | out.add(t); | |
| 73 | private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { | 75 | } |
| 74 | List<T> out = new ArrayList<>(); | 76 | Collections.sort(out); |
| 75 | for (T t : classes) { | 77 | return out; |
| 76 | out.add(t); | 78 | } |
| 77 | } | ||
| 78 | Collections.sort(out); | ||
| 79 | return out; | ||
| 80 | } | ||
| 81 | } | 79 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java index 90c096fa..d4514d42 100644 --- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | 11 | ||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | 13 | ||
| 14 | public interface MemberMapping<T extends Entry> { | 14 | public interface MemberMapping<T extends Entry> { |
| 15 | T getObfEntry(ClassEntry classEntry); | 15 | T getObfEntry(ClassEntry classEntry); |
| 16 | 16 | ||
| 17 | String getObfName(); | 17 | String getObfName(); |
| 18 | } | 18 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java index 4d7ed8f0..9c3058c4 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java | |||
| @@ -8,82 +8,83 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.utils.Utils; | 14 | import cuchaz.enigma.utils.Utils; |
| 14 | 15 | ||
| 15 | public class MethodEntry implements BehaviorEntry { | 16 | public class MethodEntry implements BehaviorEntry { |
| 16 | 17 | ||
| 17 | private ClassEntry classEntry; | 18 | private ClassEntry classEntry; |
| 18 | private String name; | 19 | private String name; |
| 19 | private Signature signature; | 20 | private Signature signature; |
| 20 | 21 | ||
| 21 | public MethodEntry(ClassEntry classEntry, String name, Signature signature) { | 22 | public MethodEntry(ClassEntry classEntry, String name, Signature signature) { |
| 22 | if (classEntry == null) { | 23 | if (classEntry == null) { |
| 23 | throw new IllegalArgumentException("Class cannot be null!"); | 24 | throw new IllegalArgumentException("Class cannot be null!"); |
| 24 | } | 25 | } |
| 25 | if (name == null) { | 26 | if (name == null) { |
| 26 | throw new IllegalArgumentException("Method name cannot be null!"); | 27 | throw new IllegalArgumentException("Method name cannot be null!"); |
| 27 | } | 28 | } |
| 28 | if (signature == null) { | 29 | if (signature == null) { |
| 29 | throw new IllegalArgumentException("Method signature cannot be null!"); | 30 | throw new IllegalArgumentException("Method signature cannot be null!"); |
| 30 | } | 31 | } |
| 31 | if (name.startsWith("<")) { | 32 | if (name.startsWith("<")) { |
| 32 | throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); | 33 | throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | this.classEntry = classEntry; | 36 | this.classEntry = classEntry; |
| 36 | this.name = name; | 37 | this.name = name; |
| 37 | this.signature = signature; | 38 | this.signature = signature; |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | public MethodEntry(MethodEntry other, String newClassName) { | 41 | public MethodEntry(MethodEntry other, String newClassName) { |
| 41 | this.classEntry = new ClassEntry(newClassName); | 42 | this.classEntry = new ClassEntry(newClassName); |
| 42 | this.name = other.name; | 43 | this.name = other.name; |
| 43 | this.signature = other.signature; | 44 | this.signature = other.signature; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | @Override | 47 | @Override |
| 47 | public ClassEntry getClassEntry() { | 48 | public ClassEntry getClassEntry() { |
| 48 | return this.classEntry; | 49 | return this.classEntry; |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | @Override | 52 | @Override |
| 52 | public String getName() { | 53 | public String getName() { |
| 53 | return this.name; | 54 | return this.name; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | @Override | 57 | @Override |
| 57 | public Signature getSignature() { | 58 | public Signature getSignature() { |
| 58 | return this.signature; | 59 | return this.signature; |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | @Override | 62 | @Override |
| 62 | public String getClassName() { | 63 | public String getClassName() { |
| 63 | return this.classEntry.getName(); | 64 | return this.classEntry.getName(); |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | @Override | 67 | @Override |
| 67 | public MethodEntry cloneToNewClass(ClassEntry classEntry) { | 68 | public MethodEntry cloneToNewClass(ClassEntry classEntry) { |
| 68 | return new MethodEntry(this, classEntry.getName()); | 69 | return new MethodEntry(this, classEntry.getName()); |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | @Override | 72 | @Override |
| 72 | public int hashCode() { | 73 | public int hashCode() { |
| 73 | return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); | 74 | return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | @Override | 77 | @Override |
| 77 | public boolean equals(Object other) { | 78 | public boolean equals(Object other) { |
| 78 | return other instanceof MethodEntry && equals((MethodEntry) other); | 79 | return other instanceof MethodEntry && equals((MethodEntry) other); |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | public boolean equals(MethodEntry other) { | 82 | public boolean equals(MethodEntry other) { |
| 82 | return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); | 83 | return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); |
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | @Override | 86 | @Override |
| 86 | public String toString() { | 87 | public String toString() { |
| 87 | return this.classEntry.getName() + "." + this.name + this.signature; | 88 | return this.classEntry.getName() + "." + this.name + this.signature; |
| 88 | } | 89 | } |
| 89 | } | 90 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java index e0aeea2d..1524ce63 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java | |||
| @@ -8,211 +8,206 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 14 | |||
| 15 | import java.util.Map; | ||
| 16 | |||
| 17 | import cuchaz.enigma.throwables.IllegalNameException; | 15 | import cuchaz.enigma.throwables.IllegalNameException; |
| 18 | import cuchaz.enigma.throwables.MappingConflict; | 16 | import cuchaz.enigma.throwables.MappingConflict; |
| 19 | 17 | ||
| 18 | import java.util.Map; | ||
| 19 | |||
| 20 | public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { | 20 | public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { |
| 21 | 21 | ||
| 22 | private String obfName; | 22 | private String obfName; |
| 23 | private String deobfName; | 23 | private String deobfName; |
| 24 | private Signature obfSignature; | 24 | private Signature obfSignature; |
| 25 | private Map<Integer, ArgumentMapping> arguments; | 25 | private Map<Integer, ArgumentMapping> arguments; |
| 26 | private Mappings.EntryModifier modifier; | 26 | private Mappings.EntryModifier modifier; |
| 27 | 27 | ||
| 28 | public MethodMapping(String obfName, Signature obfSignature) { | 28 | public MethodMapping(String obfName, Signature obfSignature) { |
| 29 | this(obfName, obfSignature, null,Mappings.EntryModifier.UNCHANGED); | 29 | this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | public MethodMapping(String obfName, Signature obfSignature, String deobfName) { | 32 | public MethodMapping(String obfName, Signature obfSignature, String deobfName) { |
| 33 | this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); | 33 | this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { | 36 | public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { |
| 37 | if (obfName == null) { | 37 | if (obfName == null) { |
| 38 | throw new IllegalArgumentException("obf name cannot be null!"); | 38 | throw new IllegalArgumentException("obf name cannot be null!"); |
| 39 | } | 39 | } |
| 40 | if (obfSignature == null) { | 40 | if (obfSignature == null) { |
| 41 | throw new IllegalArgumentException("obf signature cannot be null!"); | 41 | throw new IllegalArgumentException("obf signature cannot be null!"); |
| 42 | } | 42 | } |
| 43 | this.obfName = obfName; | 43 | this.obfName = obfName; |
| 44 | this.deobfName = NameValidator.validateMethodName(deobfName); | 44 | this.deobfName = NameValidator.validateMethodName(deobfName); |
| 45 | this.obfSignature = obfSignature; | 45 | this.obfSignature = obfSignature; |
| 46 | this.arguments = Maps.newTreeMap(); | 46 | this.arguments = Maps.newTreeMap(); |
| 47 | this.modifier = modifier; | 47 | this.modifier = modifier; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { | 50 | public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { |
| 51 | this.obfName = other.obfName; | 51 | this.obfName = other.obfName; |
| 52 | this.deobfName = other.deobfName; | 52 | this.deobfName = other.deobfName; |
| 53 | this.modifier = other.modifier; | 53 | this.modifier = other.modifier; |
| 54 | this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); | 54 | this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); |
| 55 | this.arguments = Maps.newTreeMap(); | 55 | this.arguments = Maps.newTreeMap(); |
| 56 | for (Map.Entry<Integer,ArgumentMapping> entry : other.arguments.entrySet()) { | 56 | for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) { |
| 57 | this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); | 57 | this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | @Override | 61 | @Override |
| 62 | public String getObfName() { | 62 | public String getObfName() { |
| 63 | return this.obfName; | 63 | return this.obfName; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | public String getDeobfName() { | 66 | public void setObfName(String name) { |
| 67 | return this.deobfName; | 67 | try { |
| 68 | } | 68 | NameValidator.validateMethodName(name); |
| 69 | 69 | } catch (IllegalNameException ex) { | |
| 70 | public void setDeobfName(String val) { | 70 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues |
| 71 | this.deobfName = NameValidator.validateMethodName(val); | 71 | if (this.deobfName == null) { |
| 72 | } | 72 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); |
| 73 | 73 | setDeobfName(name + "_auto_deob"); | |
| 74 | public Signature getObfSignature() { | 74 | } |
| 75 | return this.obfSignature; | 75 | } |
| 76 | } | 76 | this.obfName = name; |
| 77 | 77 | } | |
| 78 | public void setObfName(String name) { | 78 | |
| 79 | try | 79 | public String getDeobfName() { |
| 80 | { | 80 | return this.deobfName; |
| 81 | NameValidator.validateMethodName(name); | 81 | } |
| 82 | } catch (IllegalNameException ex) | 82 | |
| 83 | { | 83 | public void setDeobfName(String val) { |
| 84 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues | 84 | this.deobfName = NameValidator.validateMethodName(val); |
| 85 | if (this.deobfName == null) | 85 | } |
| 86 | { | 86 | |
| 87 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); | 87 | public Signature getObfSignature() { |
| 88 | setDeobfName(name + "_auto_deob"); | 88 | return this.obfSignature; |
| 89 | } | 89 | } |
| 90 | } | 90 | |
| 91 | this.obfName = name; | 91 | public void setObfSignature(Signature val) { |
| 92 | } | 92 | this.obfSignature = val; |
| 93 | 93 | } | |
| 94 | public void setObfSignature(Signature val) { | 94 | |
| 95 | this.obfSignature = val; | 95 | public Iterable<ArgumentMapping> arguments() { |
| 96 | } | 96 | return this.arguments.values(); |
| 97 | 97 | } | |
| 98 | public Iterable<ArgumentMapping> arguments() { | 98 | |
| 99 | return this.arguments.values(); | 99 | public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { |
| 100 | } | 100 | if (this.arguments.containsKey(argumentMapping.getIndex())) { |
| 101 | 101 | throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); | |
| 102 | public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { | 102 | } |
| 103 | if (this.arguments.containsKey(argumentMapping.getIndex())) { | 103 | this.arguments.put(argumentMapping.getIndex(), argumentMapping); |
| 104 | throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); | 104 | } |
| 105 | } | 105 | |
| 106 | this.arguments.put(argumentMapping.getIndex(), argumentMapping); | 106 | public String getObfArgumentName(int index) { |
| 107 | } | 107 | ArgumentMapping argumentMapping = this.arguments.get(index); |
| 108 | 108 | if (argumentMapping != null) { | |
| 109 | public String getObfArgumentName(int index) { | 109 | return argumentMapping.getName(); |
| 110 | ArgumentMapping argumentMapping = this.arguments.get(index); | 110 | } |
| 111 | if (argumentMapping != null) { | 111 | |
| 112 | return argumentMapping.getName(); | 112 | return null; |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | return null; | 115 | public String getDeobfArgumentName(int index) { |
| 116 | } | 116 | ArgumentMapping argumentMapping = this.arguments.get(index); |
| 117 | 117 | if (argumentMapping != null) { | |
| 118 | public String getDeobfArgumentName(int index) { | 118 | return argumentMapping.getName(); |
| 119 | ArgumentMapping argumentMapping = this.arguments.get(index); | 119 | } |
| 120 | if (argumentMapping != null) { | 120 | |
| 121 | return argumentMapping.getName(); | 121 | return null; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | return null; | 124 | public void setArgumentName(int index, String name) { |
| 125 | } | 125 | ArgumentMapping argumentMapping = this.arguments.get(index); |
| 126 | 126 | if (argumentMapping == null) { | |
| 127 | public void setArgumentName(int index, String name) { | 127 | argumentMapping = new ArgumentMapping(index, name); |
| 128 | ArgumentMapping argumentMapping = this.arguments.get(index); | 128 | boolean wasAdded = this.arguments.put(index, argumentMapping) == null; |
| 129 | if (argumentMapping == null) { | 129 | assert (wasAdded); |
| 130 | argumentMapping = new ArgumentMapping(index, name); | 130 | } else { |
| 131 | boolean wasAdded = this.arguments.put(index, argumentMapping) == null; | 131 | argumentMapping.setName(name); |
| 132 | assert (wasAdded); | 132 | } |
| 133 | } else { | 133 | } |
| 134 | argumentMapping.setName(name); | 134 | |
| 135 | } | 135 | public void removeArgumentName(int index) { |
| 136 | } | 136 | boolean wasRemoved = this.arguments.remove(index) != null; |
| 137 | 137 | assert (wasRemoved); | |
| 138 | public void removeArgumentName(int index) { | 138 | } |
| 139 | boolean wasRemoved = this.arguments.remove(index) != null; | 139 | |
| 140 | assert (wasRemoved); | 140 | @Override |
| 141 | } | 141 | public String toString() { |
| 142 | 142 | StringBuilder buf = new StringBuilder(); | |
| 143 | @Override | 143 | buf.append("\t"); |
| 144 | public String toString() { | 144 | buf.append(this.obfName); |
| 145 | StringBuilder buf = new StringBuilder(); | 145 | buf.append(" <-> "); |
| 146 | buf.append("\t"); | 146 | buf.append(this.deobfName); |
| 147 | buf.append(this.obfName); | 147 | buf.append("\n"); |
| 148 | buf.append(" <-> "); | 148 | buf.append("\t"); |
| 149 | buf.append(this.deobfName); | 149 | buf.append(this.obfSignature); |
| 150 | buf.append("\n"); | 150 | buf.append("\n"); |
| 151 | buf.append("\t"); | 151 | buf.append("\tArguments:\n"); |
| 152 | buf.append(this.obfSignature); | 152 | for (ArgumentMapping argumentMapping : this.arguments.values()) { |
| 153 | buf.append("\n"); | 153 | buf.append("\t\t"); |
| 154 | buf.append("\tArguments:\n"); | 154 | buf.append(argumentMapping.getIndex()); |
| 155 | for (ArgumentMapping argumentMapping : this.arguments.values()) { | 155 | buf.append(" -> "); |
| 156 | buf.append("\t\t"); | 156 | buf.append(argumentMapping.getName()); |
| 157 | buf.append(argumentMapping.getIndex()); | 157 | buf.append("\n"); |
| 158 | buf.append(" -> "); | 158 | } |
| 159 | buf.append(argumentMapping.getName()); | 159 | return buf.toString(); |
| 160 | buf.append("\n"); | 160 | } |
| 161 | } | 161 | |
| 162 | return buf.toString(); | 162 | @Override |
| 163 | } | 163 | public int compareTo(MethodMapping other) { |
| 164 | 164 | return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); | |
| 165 | @Override | 165 | } |
| 166 | public int compareTo(MethodMapping other) { | 166 | |
| 167 | return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); | 167 | public boolean containsArgument(String name) { |
| 168 | } | 168 | for (ArgumentMapping argumentMapping : this.arguments.values()) { |
| 169 | 169 | if (argumentMapping.getName().equals(name)) { | |
| 170 | public boolean containsArgument(String name) { | 170 | return true; |
| 171 | for (ArgumentMapping argumentMapping : this.arguments.values()) { | 171 | } |
| 172 | if (argumentMapping.getName().equals(name)) { | 172 | } |
| 173 | return true; | 173 | return false; |
| 174 | } | 174 | } |
| 175 | } | 175 | |
| 176 | return false; | 176 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { |
| 177 | } | 177 | // rename obf classes in the signature |
| 178 | 178 | Signature newSignature = new Signature(this.obfSignature, className -> | |
| 179 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | 179 | { |
| 180 | // rename obf classes in the signature | 180 | if (className.equals(oldObfClassName)) { |
| 181 | Signature newSignature = new Signature(this.obfSignature, className -> | 181 | return newObfClassName; |
| 182 | { | 182 | } |
| 183 | if (className.equals(oldObfClassName)) { | 183 | return null; |
| 184 | return newObfClassName; | 184 | }); |
| 185 | } | 185 | |
| 186 | return null; | 186 | if (!newSignature.equals(this.obfSignature)) { |
| 187 | }); | 187 | this.obfSignature = newSignature; |
| 188 | 188 | return true; | |
| 189 | if (!newSignature.equals(this.obfSignature)) { | 189 | } |
| 190 | this.obfSignature = newSignature; | 190 | return false; |
| 191 | return true; | 191 | } |
| 192 | } | 192 | |
| 193 | return false; | 193 | public boolean isConstructor() { |
| 194 | } | 194 | return this.obfName.startsWith("<"); |
| 195 | 195 | } | |
| 196 | public boolean isConstructor() { | 196 | |
| 197 | return this.obfName.startsWith("<"); | 197 | @Override |
| 198 | } | 198 | public BehaviorEntry getObfEntry(ClassEntry classEntry) { |
| 199 | 199 | if (isConstructor()) { | |
| 200 | @Override | 200 | return new ConstructorEntry(classEntry, this.obfSignature); |
| 201 | public BehaviorEntry getObfEntry(ClassEntry classEntry) { | 201 | } else { |
| 202 | if (isConstructor()) { | 202 | return new MethodEntry(classEntry, this.obfName, this.obfSignature); |
| 203 | return new ConstructorEntry(classEntry, this.obfSignature); | 203 | } |
| 204 | } else { | 204 | } |
| 205 | return new MethodEntry(classEntry, this.obfName, this.obfSignature); | 205 | |
| 206 | } | 206 | public Mappings.EntryModifier getModifier() { |
| 207 | } | 207 | return modifier; |
| 208 | 208 | } | |
| 209 | public Mappings.EntryModifier getModifier() | 209 | |
| 210 | { | 210 | public void setModifier(Mappings.EntryModifier modifier) { |
| 211 | return modifier; | 211 | this.modifier = modifier; |
| 212 | } | 212 | } |
| 213 | |||
| 214 | public void setModifier(Mappings.EntryModifier modifier) | ||
| 215 | { | ||
| 216 | this.modifier = modifier; | ||
| 217 | } | ||
| 218 | } | 213 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java index 6925b72a..aa3dc4de 100644 --- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java +++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java | |||
| @@ -8,61 +8,62 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 14 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 15 | import javassist.bytecode.Descriptor; | ||
| 16 | |||
| 13 | import java.util.Arrays; | 17 | import java.util.Arrays; |
| 14 | import java.util.List; | 18 | import java.util.List; |
| 15 | import java.util.regex.Pattern; | 19 | import java.util.regex.Pattern; |
| 16 | 20 | ||
| 17 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 18 | import javassist.bytecode.Descriptor; | ||
| 19 | |||
| 20 | public class NameValidator { | 21 | public class NameValidator { |
| 21 | 22 | ||
| 22 | private static final Pattern IdentifierPattern; | 23 | private static final Pattern IdentifierPattern; |
| 23 | private static final Pattern ClassPattern; | 24 | private static final Pattern ClassPattern; |
| 24 | private static final List<String> ReservedWords = Arrays.asList( | 25 | private static final List<String> ReservedWords = Arrays.asList( |
| 25 | "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", | 26 | "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", |
| 26 | "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", | 27 | "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", |
| 27 | "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", | 28 | "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", |
| 28 | "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", | 29 | "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", |
| 29 | "long", "strictfp", "volatile", "const", "float", "native", "super", "while" | 30 | "long", "strictfp", "volatile", "const", "float", "native", "super", "while" |
| 30 | ); | 31 | ); |
| 31 | 32 | ||
| 32 | static { | 33 | static { |
| 33 | String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; | 34 | String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; |
| 34 | IdentifierPattern = Pattern.compile(identifierRegex); | 35 | IdentifierPattern = Pattern.compile(identifierRegex); |
| 35 | ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); | 36 | ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | public static String validateClassName(String name, boolean packageRequired) { | 39 | public static String validateClassName(String name, boolean packageRequired) { |
| 39 | if (name == null) { | 40 | if (name == null) { |
| 40 | return null; | 41 | return null; |
| 41 | } | 42 | } |
| 42 | if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { | 43 | if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { |
| 43 | throw new IllegalNameException(name, "This doesn't look like a legal class name"); | 44 | throw new IllegalNameException(name, "This doesn't look like a legal class name"); |
| 44 | } | 45 | } |
| 45 | if (packageRequired && new ClassEntry(name).getPackageName() == null) { | 46 | if (packageRequired && new ClassEntry(name).getPackageName() == null) { |
| 46 | throw new IllegalNameException(name, "Class must be in a package"); | 47 | throw new IllegalNameException(name, "Class must be in a package"); |
| 47 | } | 48 | } |
| 48 | return Descriptor.toJvmName(name); | 49 | return Descriptor.toJvmName(name); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | public static String validateFieldName(String name) { | 52 | public static String validateFieldName(String name) { |
| 52 | if (name == null) { | 53 | if (name == null) { |
| 53 | return null; | 54 | return null; |
| 54 | } | 55 | } |
| 55 | if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { | 56 | if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { |
| 56 | throw new IllegalNameException(name, "This doesn't look like a legal identifier"); | 57 | throw new IllegalNameException(name, "This doesn't look like a legal identifier"); |
| 57 | } | 58 | } |
| 58 | return name; | 59 | return name; |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | public static String validateMethodName(String name) { | 62 | public static String validateMethodName(String name) { |
| 62 | return validateFieldName(name); | 63 | return validateFieldName(name); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | public static String validateArgumentName(String name) { | 66 | public static String validateArgumentName(String name) { |
| 66 | return validateFieldName(name); | 67 | return validateFieldName(name); |
| 67 | } | 68 | } |
| 68 | } | 69 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java index 51fed83e..33d930dc 100644 --- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.strobel.assembler.metadata.*; | 14 | import com.strobel.assembler.metadata.*; |
| @@ -16,57 +17,51 @@ import java.util.List; | |||
| 16 | 17 | ||
| 17 | public class ProcyonEntryFactory { | 18 | public class ProcyonEntryFactory { |
| 18 | 19 | ||
| 19 | private static String getErasedSignature(MemberReference def) | 20 | private static String getErasedSignature(MemberReference def) { |
| 20 | { | 21 | if (!(def instanceof MethodReference)) |
| 21 | if (!(def instanceof MethodReference)) | 22 | return def.getErasedSignature(); |
| 22 | return def.getErasedSignature(); | 23 | MethodReference methodReference = (MethodReference) def; |
| 23 | MethodReference methodReference = (MethodReference) def; | 24 | StringBuilder builder = new StringBuilder("("); |
| 24 | StringBuilder builder = new StringBuilder("("); | 25 | for (ParameterDefinition param : methodReference.getParameters()) { |
| 25 | for (ParameterDefinition param : methodReference.getParameters()) | 26 | TypeReference paramType = param.getParameterType(); |
| 26 | { | 27 | if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) { |
| 27 | TypeReference paramType = param.getParameterType(); | 28 | List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces(); |
| 28 | if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) | 29 | interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); |
| 29 | { | 30 | } else |
| 30 | List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces(); | 31 | builder.append(paramType.getErasedSignature()); |
| 31 | interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); | 32 | } |
| 32 | } | 33 | builder.append(")"); |
| 33 | else | ||
| 34 | builder.append(paramType.getErasedSignature()); | ||
| 35 | } | ||
| 36 | builder.append(")"); | ||
| 37 | 34 | ||
| 38 | TypeReference returnType = methodReference.getReturnType(); | 35 | TypeReference returnType = methodReference.getReturnType(); |
| 39 | if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) | 36 | if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) { |
| 40 | { | 37 | List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces(); |
| 41 | List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces(); | 38 | interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); |
| 42 | interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); | 39 | } else |
| 43 | } | 40 | builder.append(returnType.getErasedSignature()); |
| 44 | else | 41 | return builder.toString(); |
| 45 | builder.append(returnType.getErasedSignature()); | 42 | } |
| 46 | return builder.toString(); | ||
| 47 | } | ||
| 48 | 43 | ||
| 49 | public static FieldEntry getFieldEntry(MemberReference def) { | 44 | public static FieldEntry getFieldEntry(MemberReference def) { |
| 50 | return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); | 45 | return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); |
| 51 | } | 46 | } |
| 52 | 47 | ||
| 53 | public static MethodEntry getMethodEntry(MemberReference def) { | 48 | public static MethodEntry getMethodEntry(MemberReference def) { |
| 54 | return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); | 49 | return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); |
| 55 | } | 50 | } |
| 56 | 51 | ||
| 57 | public static ConstructorEntry getConstructorEntry(MethodReference def) { | 52 | public static ConstructorEntry getConstructorEntry(MethodReference def) { |
| 58 | if (def.isTypeInitializer()) { | 53 | if (def.isTypeInitializer()) { |
| 59 | return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); | 54 | return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); |
| 60 | } else { | 55 | } else { |
| 61 | return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); | 56 | return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); |
| 62 | } | 57 | } |
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | public static BehaviorEntry getBehaviorEntry(MethodReference def) { | 60 | public static BehaviorEntry getBehaviorEntry(MethodReference def) { |
| 66 | if (def.isConstructor() || def.isTypeInitializer()) { | 61 | if (def.isConstructor() || def.isTypeInitializer()) { |
| 67 | return getConstructorEntry(def); | 62 | return getConstructorEntry(def); |
| 68 | } else { | 63 | } else { |
| 69 | return getMethodEntry(def); | 64 | return getMethodEntry(def); |
| 70 | } | 65 | } |
| 71 | } | 66 | } |
| 72 | } | 67 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java index f30b6069..78130d6b 100644 --- a/src/main/java/cuchaz/enigma/mapping/Signature.java +++ b/src/main/java/cuchaz/enigma/mapping/Signature.java | |||
| @@ -8,99 +8,99 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.utils.Utils; | ||
| 14 | 16 | ||
| 15 | import java.util.List; | 17 | import java.util.List; |
| 16 | 18 | ||
| 17 | import cuchaz.enigma.utils.Utils; | ||
| 18 | |||
| 19 | public class Signature { | 19 | public class Signature { |
| 20 | 20 | ||
| 21 | private List<Type> argumentTypes; | 21 | private List<Type> argumentTypes; |
| 22 | private Type returnType; | 22 | private Type returnType; |
| 23 | 23 | ||
| 24 | public Signature(String signature) { | 24 | public Signature(String signature) { |
| 25 | try { | 25 | try { |
| 26 | this.argumentTypes = Lists.newArrayList(); | 26 | this.argumentTypes = Lists.newArrayList(); |
| 27 | int i = 0; | 27 | int i = 0; |
| 28 | while (i < signature.length()) { | 28 | while (i < signature.length()) { |
| 29 | char c = signature.charAt(i); | 29 | char c = signature.charAt(i); |
| 30 | if (c == '(') { | 30 | if (c == '(') { |
| 31 | assert (this.argumentTypes.isEmpty()); | 31 | assert (this.argumentTypes.isEmpty()); |
| 32 | assert (this.returnType == null); | 32 | assert (this.returnType == null); |
| 33 | i++; | 33 | i++; |
| 34 | } else if (c == ')') { | 34 | } else if (c == ')') { |
| 35 | i++; | 35 | i++; |
| 36 | break; | 36 | break; |
| 37 | } else { | 37 | } else { |
| 38 | String type = Type.parseFirst(signature.substring(i)); | 38 | String type = Type.parseFirst(signature.substring(i)); |
| 39 | this.argumentTypes.add(new Type(type)); | 39 | this.argumentTypes.add(new Type(type)); |
| 40 | i += type.length(); | 40 | i += type.length(); |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | this.returnType = new Type(Type.parseFirst(signature.substring(i))); | 43 | this.returnType = new Type(Type.parseFirst(signature.substring(i))); |
| 44 | } catch (Exception ex) { | 44 | } catch (Exception ex) { |
| 45 | throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); | 45 | throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | public Signature(Signature other, ClassNameReplacer replacer) { | 49 | public Signature(Signature other, ClassNameReplacer replacer) { |
| 50 | this.argumentTypes = Lists.newArrayList(other.argumentTypes); | 50 | this.argumentTypes = Lists.newArrayList(other.argumentTypes); |
| 51 | for (int i = 0; i < this.argumentTypes.size(); i++) { | 51 | for (int i = 0; i < this.argumentTypes.size(); i++) { |
| 52 | this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); | 52 | this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); |
| 53 | } | 53 | } |
| 54 | this.returnType = new Type(other.returnType, replacer); | 54 | this.returnType = new Type(other.returnType, replacer); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | public List<Type> getArgumentTypes() { | 57 | public List<Type> getArgumentTypes() { |
| 58 | return this.argumentTypes; | 58 | return this.argumentTypes; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | public Type getReturnType() { | 61 | public Type getReturnType() { |
| 62 | return this.returnType; | 62 | return this.returnType; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | @Override | 65 | @Override |
| 66 | public String toString() { | 66 | public String toString() { |
| 67 | StringBuilder buf = new StringBuilder(); | 67 | StringBuilder buf = new StringBuilder(); |
| 68 | buf.append("("); | 68 | buf.append("("); |
| 69 | for (Type type : this.argumentTypes) { | 69 | for (Type type : this.argumentTypes) { |
| 70 | buf.append(type.toString()); | 70 | buf.append(type); |
| 71 | } | 71 | } |
| 72 | buf.append(")"); | 72 | buf.append(")"); |
| 73 | buf.append(this.returnType.toString()); | 73 | buf.append(this.returnType); |
| 74 | return buf.toString(); | 74 | return buf.toString(); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | public Iterable<Type> types() { | 77 | public Iterable<Type> types() { |
| 78 | List<Type> types = Lists.newArrayList(); | 78 | List<Type> types = Lists.newArrayList(); |
| 79 | types.addAll(this.argumentTypes); | 79 | types.addAll(this.argumentTypes); |
| 80 | types.add(this.returnType); | 80 | types.add(this.returnType); |
| 81 | return types; | 81 | return types; |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | @Override | 84 | @Override |
| 85 | public boolean equals(Object other) { | 85 | public boolean equals(Object other) { |
| 86 | return other instanceof Signature && equals((Signature) other); | 86 | return other instanceof Signature && equals((Signature) other); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | public boolean equals(Signature other) { | 89 | public boolean equals(Signature other) { |
| 90 | return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); | 90 | return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | @Override | 93 | @Override |
| 94 | public int hashCode() { | 94 | public int hashCode() { |
| 95 | return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); | 95 | return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | public boolean hasClass(ClassEntry classEntry) { | 98 | public boolean hasClass(ClassEntry classEntry) { |
| 99 | for (Type type : types()) { | 99 | for (Type type : types()) { |
| 100 | if (type.hasClass() && type.getClassEntry().equals(classEntry)) { | 100 | if (type.hasClass() && type.getClassEntry().equals(classEntry)) { |
| 101 | return true; | 101 | return true; |
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | return false; | 104 | return false; |
| 105 | } | 105 | } |
| 106 | } | 106 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java index 98643330..ddc5af4b 100644 --- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| @@ -18,74 +19,74 @@ import java.util.List; | |||
| 18 | 19 | ||
| 19 | public class SignatureUpdater { | 20 | public class SignatureUpdater { |
| 20 | 21 | ||
| 21 | public interface ClassNameUpdater { | 22 | public static String update(String signature, ClassNameUpdater updater) { |
| 22 | String update(String className); | 23 | try { |
| 23 | } | 24 | StringBuilder buf = new StringBuilder(); |
| 24 | 25 | ||
| 25 | public static String update(String signature, ClassNameUpdater updater) { | 26 | // read the signature character-by-character |
| 26 | try { | 27 | StringReader reader = new StringReader(signature); |
| 27 | StringBuilder buf = new StringBuilder(); | 28 | int i; |
| 29 | while ((i = reader.read()) != -1) { | ||
| 30 | char c = (char) i; | ||
| 28 | 31 | ||
| 29 | // read the signature character-by-character | 32 | // does this character start a class name? |
| 30 | StringReader reader = new StringReader(signature); | 33 | if (c == 'L') { |
| 31 | int i; | 34 | // update the class name and add it to the buffer |
| 32 | while ((i = reader.read()) != -1) { | 35 | buf.append('L'); |
| 33 | char c = (char) i; | 36 | String className = readClass(reader); |
| 37 | if (className == null) { | ||
| 38 | throw new IllegalArgumentException("Malformed signature: " + signature); | ||
| 39 | } | ||
| 40 | buf.append(updater.update(className)); | ||
| 41 | buf.append(';'); | ||
| 42 | } else { | ||
| 43 | // copy the character into the buffer | ||
| 44 | buf.append(c); | ||
| 45 | } | ||
| 46 | } | ||
| 34 | 47 | ||
| 35 | // does this character start a class name? | 48 | return buf.toString(); |
| 36 | if (c == 'L') { | 49 | } catch (IOException ex) { |
| 37 | // update the class name and add it to the buffer | 50 | // I'm pretty sure a StringReader will never throw one of these |
| 38 | buf.append('L'); | 51 | throw new Error(ex); |
| 39 | String className = readClass(reader); | 52 | } |
| 40 | if (className == null) { | 53 | } |
| 41 | throw new IllegalArgumentException("Malformed signature: " + signature); | ||
| 42 | } | ||
| 43 | buf.append(updater.update(className)); | ||
| 44 | buf.append(';'); | ||
| 45 | } else { | ||
| 46 | // copy the character into the buffer | ||
| 47 | buf.append(c); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | 54 | ||
| 51 | return buf.toString(); | 55 | private static String readClass(StringReader reader) throws IOException { |
| 52 | } catch (IOException ex) { | 56 | // read all the characters in the buffer until we hit a ';' |
| 53 | // I'm pretty sure a StringReader will never throw one of these | 57 | // remember to treat generics correctly |
| 54 | throw new Error(ex); | 58 | StringBuilder buf = new StringBuilder(); |
| 55 | } | 59 | int depth = 0; |
| 56 | } | 60 | int i; |
| 61 | while ((i = reader.read()) != -1) { | ||
| 62 | char c = (char) i; | ||
| 57 | 63 | ||
| 58 | private static String readClass(StringReader reader) throws IOException { | 64 | if (c == '<') { |
| 59 | // read all the characters in the buffer until we hit a ';' | 65 | depth++; |
| 60 | // remember to treat generics correctly | 66 | } else if (c == '>') { |
| 61 | StringBuilder buf = new StringBuilder(); | 67 | depth--; |
| 62 | int depth = 0; | 68 | } else if (depth == 0) { |
| 63 | int i; | 69 | if (c == ';') { |
| 64 | while ((i = reader.read()) != -1) { | 70 | return buf.toString(); |
| 65 | char c = (char) i; | 71 | } else { |
| 72 | buf.append(c); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 66 | 76 | ||
| 67 | if (c == '<') { | 77 | return null; |
| 68 | depth++; | 78 | } |
| 69 | } else if (c == '>') { | ||
| 70 | depth--; | ||
| 71 | } else if (depth == 0) { | ||
| 72 | if (c == ';') { | ||
| 73 | return buf.toString(); | ||
| 74 | } else { | ||
| 75 | buf.append(c); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | 79 | ||
| 80 | return null; | 80 | public static List<String> getClasses(String signature) { |
| 81 | } | 81 | final List<String> classNames = Lists.newArrayList(); |
| 82 | update(signature, className -> { | ||
| 83 | classNames.add(className); | ||
| 84 | return className; | ||
| 85 | }); | ||
| 86 | return classNames; | ||
| 87 | } | ||
| 82 | 88 | ||
| 83 | public static List<String> getClasses(String signature) { | 89 | public interface ClassNameUpdater { |
| 84 | final List<String> classNames = Lists.newArrayList(); | 90 | String update(String className); |
| 85 | update(signature, className -> { | 91 | } |
| 86 | classNames.add(className); | ||
| 87 | return className; | ||
| 88 | }); | ||
| 89 | return classNames; | ||
| 90 | } | ||
| 91 | } | 92 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java index 8329d0d2..17e31876 100644 --- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java | |||
| @@ -8,22 +8,23 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | public enum TranslationDirection { | 14 | public enum TranslationDirection { |
| 14 | 15 | ||
| 15 | Deobfuscating { | 16 | Deobfuscating { |
| 16 | @Override | 17 | @Override |
| 17 | public <T> T choose(T deobfChoice, T obfChoice) { | 18 | public <T> T choose(T deobfChoice, T obfChoice) { |
| 18 | return deobfChoice; | 19 | return deobfChoice; |
| 19 | } | 20 | } |
| 20 | }, | 21 | }, |
| 21 | Obfuscating { | 22 | Obfuscating { |
| 22 | @Override | 23 | @Override |
| 23 | public <T> T choose(T deobfChoice, T obfChoice) { | 24 | public <T> T choose(T deobfChoice, T obfChoice) { |
| 24 | return obfChoice; | 25 | return obfChoice; |
| 25 | } | 26 | } |
| 26 | }; | 27 | }; |
| 27 | 28 | ||
| 28 | public abstract <T> T choose(T deobfChoice, T obfChoice); | 29 | public abstract <T> T choose(T deobfChoice, T obfChoice); |
| 29 | } | 30 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java index e94009ee..8d464fc4 100644 --- a/src/main/java/cuchaz/enigma/mapping/Translator.java +++ b/src/main/java/cuchaz/enigma/mapping/Translator.java | |||
| @@ -8,348 +8,335 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 15 | 17 | ||
| 16 | import java.util.List; | 18 | import java.util.List; |
| 17 | import java.util.Map; | 19 | import java.util.Map; |
| 18 | 20 | ||
| 19 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 20 | |||
| 21 | public class Translator { | 21 | public class Translator { |
| 22 | 22 | ||
| 23 | private TranslationDirection direction; | 23 | private TranslationDirection direction; |
| 24 | private Map<String, ClassMapping> classes; | 24 | private Map<String, ClassMapping> classes; |
| 25 | private TranslationIndex index; | 25 | private TranslationIndex index; |
| 26 | 26 | ||
| 27 | private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); | 27 | private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); |
| 28 | 28 | ||
| 29 | public Translator() { | 29 | public Translator() { |
| 30 | this.direction = null; | 30 | this.direction = null; |
| 31 | this.classes = Maps.newHashMap(); | 31 | this.classes = Maps.newHashMap(); |
| 32 | this.index = new TranslationIndex(); | 32 | this.index = new TranslationIndex(); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { | 35 | public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { |
| 36 | this.direction = direction; | 36 | this.direction = direction; |
| 37 | this.classes = classes; | 37 | this.classes = classes; |
| 38 | this.index = index; | 38 | this.index = index; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | public TranslationDirection getDirection() { | 41 | public TranslationDirection getDirection() { |
| 42 | return direction; | 42 | return direction; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | public TranslationIndex getTranslationIndex() { | 45 | public TranslationIndex getTranslationIndex() { |
| 46 | return index; | 46 | return index; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | @SuppressWarnings("unchecked") | 49 | @SuppressWarnings("unchecked") |
| 50 | public <T extends Entry> T translateEntry(T entry) { | 50 | public <T extends Entry> T translateEntry(T entry) { |
| 51 | if (entry instanceof ClassEntry) { | 51 | if (entry instanceof ClassEntry) { |
| 52 | return (T) translateEntry((ClassEntry) entry); | 52 | return (T) translateEntry((ClassEntry) entry); |
| 53 | } else if (entry instanceof FieldEntry) { | 53 | } else if (entry instanceof FieldEntry) { |
| 54 | return (T) translateEntry((FieldEntry) entry); | 54 | return (T) translateEntry((FieldEntry) entry); |
| 55 | } else if (entry instanceof MethodEntry) { | 55 | } else if (entry instanceof MethodEntry) { |
| 56 | return (T) translateEntry((MethodEntry) entry); | 56 | return (T) translateEntry((MethodEntry) entry); |
| 57 | } else if (entry instanceof ConstructorEntry) { | 57 | } else if (entry instanceof ConstructorEntry) { |
| 58 | return (T) translateEntry((ConstructorEntry) entry); | 58 | return (T) translateEntry((ConstructorEntry) entry); |
| 59 | } else if (entry instanceof ArgumentEntry) { | 59 | } else if (entry instanceof ArgumentEntry) { |
| 60 | return (T) translateEntry((ArgumentEntry) entry); | 60 | return (T) translateEntry((ArgumentEntry) entry); |
| 61 | } else if (entry instanceof LocalVariableEntry) { | 61 | } else if (entry instanceof LocalVariableEntry) { |
| 62 | return (T) translateEntry((LocalVariableEntry) entry); | 62 | return (T) translateEntry((LocalVariableEntry) entry); |
| 63 | } else { | 63 | } else { |
| 64 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | 64 | throw new Error("Unknown entry type: " + entry.getClass().getName()); |
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | public <T extends Entry> String translate(T entry) { | 68 | public <T extends Entry> String translate(T entry) { |
| 69 | if (entry instanceof ClassEntry) { | 69 | if (entry instanceof ClassEntry) { |
| 70 | return translate((ClassEntry) entry); | 70 | return translate((ClassEntry) entry); |
| 71 | } else if (entry instanceof FieldEntry) { | 71 | } else if (entry instanceof FieldEntry) { |
| 72 | return translate((FieldEntry) entry); | 72 | return translate((FieldEntry) entry); |
| 73 | } else if (entry instanceof MethodEntry) { | 73 | } else if (entry instanceof MethodEntry) { |
| 74 | return translate((MethodEntry) entry); | 74 | return translate((MethodEntry) entry); |
| 75 | } else if (entry instanceof ConstructorEntry) { | 75 | } else if (entry instanceof ConstructorEntry) { |
| 76 | return translate(entry); | 76 | return translate(entry); |
| 77 | } else if (entry instanceof ArgumentEntry) { | 77 | } else if (entry instanceof ArgumentEntry) { |
| 78 | return translate((ArgumentEntry) entry); | 78 | return translate((ArgumentEntry) entry); |
| 79 | } else if (entry instanceof LocalVariableEntry) { | 79 | } else if (entry instanceof LocalVariableEntry) { |
| 80 | return translate((LocalVariableEntry) entry); | 80 | return translate((LocalVariableEntry) entry); |
| 81 | } else { | 81 | } else { |
| 82 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | 82 | throw new Error("Unknown entry type: " + entry.getClass().getName()); |
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | public String translate(LocalVariableEntry in) | 86 | public String translate(LocalVariableEntry in) { |
| 87 | { | 87 | LocalVariableEntry translated = translateEntry(in); |
| 88 | LocalVariableEntry translated = translateEntry(in); | 88 | if (translated.equals(in)) { |
| 89 | if (translated.equals(in)) { | 89 | return null; |
| 90 | return null; | 90 | } |
| 91 | } | 91 | return translated.getName(); |
| 92 | return translated.getName(); | 92 | } |
| 93 | } | 93 | |
| 94 | 94 | public LocalVariableEntry translateEntry(LocalVariableEntry in) { | |
| 95 | public LocalVariableEntry translateEntry(LocalVariableEntry in) | 95 | // TODO: Implement it |
| 96 | { | 96 | return in; |
| 97 | // TODO: Implement it | 97 | } |
| 98 | return in; | 98 | |
| 99 | } | 99 | public String translate(ClassEntry in) { |
| 100 | 100 | ClassEntry translated = translateEntry(in); | |
| 101 | public String translate(ClassEntry in) { | 101 | if (translated.equals(in)) { |
| 102 | ClassEntry translated = translateEntry(in); | 102 | return null; |
| 103 | if (translated.equals(in)) { | 103 | } |
| 104 | return null; | 104 | return translated.getName(); |
| 105 | } | 105 | } |
| 106 | return translated.getName(); | 106 | |
| 107 | } | 107 | public String translateClass(String className) { |
| 108 | 108 | return translate(new ClassEntry(className)); | |
| 109 | public String translateClass(String className) { | 109 | } |
| 110 | return translate(new ClassEntry(className)); | 110 | |
| 111 | } | 111 | public ClassEntry translateEntry(ClassEntry in) { |
| 112 | 112 | ||
| 113 | public ClassEntry translateEntry(ClassEntry in) { | 113 | if (in.isInnerClass()) { |
| 114 | 114 | ||
| 115 | if (in.isInnerClass()) { | 115 | // translate as much of the class chain as we can |
| 116 | 116 | List<ClassMapping> mappingsChain = getClassMappingChain(in); | |
| 117 | // translate as much of the class chain as we can | 117 | String[] obfClassNames = in.getName().split("\\$"); |
| 118 | List<ClassMapping> mappingsChain = getClassMappingChain(in); | 118 | StringBuilder buf = new StringBuilder(); |
| 119 | String[] obfClassNames = in.getName().split("\\$"); | 119 | for (int i = 0; i < obfClassNames.length; i++) { |
| 120 | StringBuilder buf = new StringBuilder(); | 120 | boolean isFirstClass = buf.length() == 0; |
| 121 | for (int i = 0; i < obfClassNames.length; i++) { | 121 | String className = null; |
| 122 | boolean isFirstClass = buf.length() == 0; | 122 | ClassMapping classMapping = mappingsChain.get(i); |
| 123 | String className = null; | 123 | if (classMapping != null) { |
| 124 | ClassMapping classMapping = mappingsChain.get(i); | 124 | className = this.direction.choose( |
| 125 | if (classMapping != null) { | 125 | classMapping.getDeobfName(), |
| 126 | className = this.direction.choose( | 126 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() |
| 127 | classMapping.getDeobfName(), | 127 | ); |
| 128 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() | 128 | } |
| 129 | ); | 129 | if (className == null) { |
| 130 | } | 130 | className = obfClassNames[i]; |
| 131 | if (className == null) { | 131 | } |
| 132 | className = obfClassNames[i]; | 132 | if (!isFirstClass) { |
| 133 | } | 133 | buf.append("$"); |
| 134 | if (!isFirstClass) { | 134 | } |
| 135 | buf.append("$"); | 135 | buf.append(className); |
| 136 | } | 136 | } |
| 137 | buf.append(className); | 137 | return new ClassEntry(buf.toString()); |
| 138 | } | 138 | |
| 139 | return new ClassEntry(buf.toString()); | 139 | } else { |
| 140 | 140 | ||
| 141 | } else { | 141 | // normal classes are easy |
| 142 | 142 | ClassMapping classMapping = this.classes.get(in.getName()); | |
| 143 | // normal classes are easy | 143 | if (classMapping == null) { |
| 144 | ClassMapping classMapping = this.classes.get(in.getName()); | 144 | return in; |
| 145 | if (classMapping == null) { | 145 | } |
| 146 | return in; | 146 | return this.direction.choose( |
| 147 | } | 147 | classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, |
| 148 | return this.direction.choose( | 148 | new ClassEntry(classMapping.getObfFullName()) |
| 149 | classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, | 149 | ); |
| 150 | new ClassEntry(classMapping.getObfFullName()) | 150 | } |
| 151 | ); | 151 | } |
| 152 | } | 152 | |
| 153 | } | 153 | public String translate(FieldEntry in) { |
| 154 | 154 | ||
| 155 | public String translate(FieldEntry in) { | 155 | // resolve the class entry |
| 156 | 156 | ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); | |
| 157 | // resolve the class entry | 157 | if (resolvedClassEntry != null) { |
| 158 | ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); | 158 | |
| 159 | if (resolvedClassEntry != null) { | 159 | // look for the class |
| 160 | 160 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | |
| 161 | // look for the class | 161 | if (classMapping != null) { |
| 162 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | 162 | |
| 163 | if (classMapping != null) { | 163 | // look for the field |
| 164 | 164 | String translatedName = this.direction.choose( | |
| 165 | // look for the field | 165 | classMapping.getDeobfFieldName(in.getName(), in.getType()), |
| 166 | String translatedName = this.direction.choose( | 166 | classMapping.getObfFieldName(in.getName(), translateType(in.getType())) |
| 167 | classMapping.getDeobfFieldName(in.getName(), in.getType()), | 167 | ); |
| 168 | classMapping.getObfFieldName(in.getName(), translateType(in.getType())) | 168 | if (translatedName != null) { |
| 169 | ); | 169 | return translatedName; |
| 170 | if (translatedName != null) { | 170 | } |
| 171 | return translatedName; | 171 | } |
| 172 | } | 172 | } |
| 173 | } | 173 | return null; |
| 174 | } | 174 | } |
| 175 | return null; | 175 | |
| 176 | } | 176 | public FieldEntry translateEntry(FieldEntry in) { |
| 177 | 177 | String name = translate(in); | |
| 178 | public FieldEntry translateEntry(FieldEntry in) { | 178 | if (name == null) { |
| 179 | String name = translate(in); | 179 | name = in.getName(); |
| 180 | if (name == null) { | 180 | } |
| 181 | name = in.getName(); | 181 | return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); |
| 182 | } | 182 | } |
| 183 | return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); | 183 | |
| 184 | } | 184 | public String translate(MethodEntry in) { |
| 185 | 185 | // resolve the class entry | |
| 186 | public String translate(MethodEntry in) { | 186 | ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); |
| 187 | // resolve the class entry | 187 | if (resolvedClassEntry != null) { |
| 188 | ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); | 188 | |
| 189 | if (resolvedClassEntry != null) { | 189 | // look for class |
| 190 | 190 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | |
| 191 | // look for class | 191 | if (classMapping != null) { |
| 192 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | 192 | |
| 193 | if (classMapping != null) { | 193 | // look for the method |
| 194 | 194 | MethodMapping methodMapping = this.direction.choose( | |
| 195 | // look for the method | 195 | classMapping.getMethodByObf(in.getName(), in.getSignature()), |
| 196 | MethodMapping methodMapping = this.direction.choose( | 196 | classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) |
| 197 | classMapping.getMethodByObf(in.getName(), in.getSignature()), | 197 | ); |
| 198 | classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) | 198 | if (methodMapping != null) { |
| 199 | ); | 199 | return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); |
| 200 | if (methodMapping != null) { | 200 | } |
| 201 | return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); | 201 | } |
| 202 | } | 202 | } |
| 203 | } | 203 | return null; |
| 204 | } | 204 | } |
| 205 | return null; | 205 | |
| 206 | } | 206 | public MethodEntry translateEntry(MethodEntry in) { |
| 207 | 207 | String name = translate(in); | |
| 208 | public MethodEntry translateEntry(MethodEntry in) { | 208 | if (name == null) { |
| 209 | String name = translate(in); | 209 | name = in.getName(); |
| 210 | if (name == null) { | 210 | } |
| 211 | name = in.getName(); | 211 | return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); |
| 212 | } | 212 | } |
| 213 | return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); | 213 | |
| 214 | } | 214 | public ConstructorEntry translateEntry(ConstructorEntry in) { |
| 215 | 215 | if (in.isStatic()) { | |
| 216 | public ConstructorEntry translateEntry(ConstructorEntry in) { | 216 | return new ConstructorEntry(translateEntry(in.getClassEntry())); |
| 217 | if (in.isStatic()) { | 217 | } else { |
| 218 | return new ConstructorEntry(translateEntry(in.getClassEntry())); | 218 | return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); |
| 219 | } else { | 219 | } |
| 220 | return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); | 220 | } |
| 221 | } | 221 | |
| 222 | } | 222 | public BehaviorEntry translateEntry(BehaviorEntry in) { |
| 223 | 223 | if (in instanceof MethodEntry) { | |
| 224 | public BehaviorEntry translateEntry(BehaviorEntry in) { | 224 | return translateEntry((MethodEntry) in); |
| 225 | if (in instanceof MethodEntry) { | 225 | } else if (in instanceof ConstructorEntry) { |
| 226 | return translateEntry((MethodEntry) in); | 226 | return translateEntry((ConstructorEntry) in); |
| 227 | } else if (in instanceof ConstructorEntry) { | 227 | } |
| 228 | return translateEntry((ConstructorEntry) in); | 228 | throw new Error("Wrong entry type!"); |
| 229 | } | 229 | } |
| 230 | throw new Error("Wrong entry type!"); | 230 | |
| 231 | } | 231 | // TODO: support not identical behavior (specific to constructor) |
| 232 | 232 | public String translate(ArgumentEntry in) { | |
| 233 | // TODO: support not identical behavior (specific to constructor) | 233 | String classTranslate = translateArgument(in); |
| 234 | public String translate(ArgumentEntry in) | 234 | |
| 235 | { | 235 | // Not found in this class |
| 236 | String classTranslate = translateArgument(in); | 236 | if (classTranslate == null) { |
| 237 | 237 | List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry()); | |
| 238 | // Not found in this class | 238 | |
| 239 | if (classTranslate == null) | 239 | // Check in mother class for the arg |
| 240 | { | 240 | for (ClassEntry entry : ancestry) { |
| 241 | List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry()); | 241 | ArgumentEntry motherArg = in.cloneToNewClass(entry); |
| 242 | 242 | if (this.index.entryExists(motherArg)) { | |
| 243 | // Check in mother class for the arg | 243 | String result = translateArgument(motherArg); |
| 244 | for (ClassEntry entry : ancestry) | 244 | if (result != null) |
| 245 | { | 245 | return result; |
| 246 | ArgumentEntry motherArg = in.cloneToNewClass(entry); | 246 | } |
| 247 | if (this.index.entryExists(motherArg)) | 247 | } |
| 248 | { | 248 | } |
| 249 | String result = translateArgument(motherArg); | 249 | return classTranslate; |
| 250 | if (result != null) | 250 | } |
| 251 | return result; | 251 | |
| 252 | } | 252 | public String translateArgument(ArgumentEntry in) { |
| 253 | } | 253 | // look for identical behavior in superclasses |
| 254 | } | 254 | ClassEntry entry = in.getClassEntry(); |
| 255 | return classTranslate; | 255 | |
| 256 | } | 256 | if (entry != null) { |
| 257 | 257 | // look for the class | |
| 258 | public String translateArgument(ArgumentEntry in) { | 258 | ClassMapping classMapping = findClassMapping(entry); |
| 259 | // look for identical behavior in superclasses | 259 | if (classMapping != null) { |
| 260 | ClassEntry entry = in.getClassEntry(); | 260 | |
| 261 | 261 | // look for the method | |
| 262 | if (entry != null) | 262 | MethodMapping methodMapping = this.direction.choose( |
| 263 | { | 263 | classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), |
| 264 | // look for the class | 264 | classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) |
| 265 | ClassMapping classMapping = findClassMapping(entry); | 265 | ); |
| 266 | if (classMapping != null) { | 266 | if (methodMapping != null) { |
| 267 | 267 | return this.direction.choose( | |
| 268 | // look for the method | 268 | methodMapping.getDeobfArgumentName(in.getIndex()), |
| 269 | MethodMapping methodMapping = this.direction.choose( | 269 | methodMapping.getObfArgumentName(in.getIndex()) |
| 270 | classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), | 270 | ); |
| 271 | classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) | 271 | } |
| 272 | ); | 272 | } |
| 273 | if (methodMapping != null) { | 273 | } |
| 274 | return this.direction.choose( | 274 | return null; |
| 275 | methodMapping.getDeobfArgumentName(in.getIndex()), | 275 | } |
| 276 | methodMapping.getObfArgumentName(in.getIndex()) | 276 | |
| 277 | ); | 277 | public ArgumentEntry translateEntry(ArgumentEntry in) { |
| 278 | } | 278 | String name = translate(in); |
| 279 | } | 279 | if (name == null) { |
| 280 | } | 280 | name = in.getName(); |
| 281 | return null; | 281 | } |
| 282 | } | 282 | return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); |
| 283 | 283 | } | |
| 284 | public ArgumentEntry translateEntry(ArgumentEntry in) { | 284 | |
| 285 | String name = translate(in); | 285 | public Type translateType(Type type) { |
| 286 | if (name == null) { | 286 | return new Type(type, this.classNameReplacer); |
| 287 | name = in.getName(); | 287 | } |
| 288 | } | 288 | |
| 289 | return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); | 289 | public Signature translateSignature(Signature signature) { |
| 290 | } | 290 | return new Signature(signature, this.classNameReplacer); |
| 291 | 291 | } | |
| 292 | public Type translateType(Type type) { | 292 | |
| 293 | return new Type(type, this.classNameReplacer); | 293 | private ClassMapping findClassMapping(ClassEntry in) { |
| 294 | } | 294 | List<ClassMapping> mappingChain = getClassMappingChain(in); |
| 295 | 295 | return mappingChain.get(mappingChain.size() - 1); | |
| 296 | public Signature translateSignature(Signature signature) { | 296 | } |
| 297 | return new Signature(signature, this.classNameReplacer); | 297 | |
| 298 | } | 298 | private List<ClassMapping> getClassMappingChain(ClassEntry in) { |
| 299 | 299 | ||
| 300 | private ClassMapping findClassMapping(ClassEntry in) { | 300 | // get a list of all the classes in the hierarchy |
| 301 | List<ClassMapping> mappingChain = getClassMappingChain(in); | 301 | String[] parts = in.getName().split("\\$"); |
| 302 | return mappingChain.get(mappingChain.size() - 1); | 302 | List<ClassMapping> mappingsChain = Lists.newArrayList(); |
| 303 | } | 303 | |
| 304 | 304 | // get mappings for the outer class | |
| 305 | private List<ClassMapping> getClassMappingChain(ClassEntry in) { | 305 | ClassMapping outerClassMapping = this.classes.get(parts[0]); |
| 306 | 306 | mappingsChain.add(outerClassMapping); | |
| 307 | // get a list of all the classes in the hierarchy | 307 | |
| 308 | String[] parts = in.getName().split("\\$"); | 308 | for (int i = 1; i < parts.length; i++) { |
| 309 | List<ClassMapping> mappingsChain = Lists.newArrayList(); | 309 | |
| 310 | 310 | // get mappings for the inner class | |
| 311 | // get mappings for the outer class | 311 | ClassMapping innerClassMapping = null; |
| 312 | ClassMapping outerClassMapping = this.classes.get(parts[0]); | 312 | if (outerClassMapping != null) { |
| 313 | mappingsChain.add(outerClassMapping); | 313 | innerClassMapping = this.direction.choose( |
| 314 | 314 | outerClassMapping.getInnerClassByObfSimple(parts[i]), | |
| 315 | for (int i = 1; i < parts.length; i++) { | 315 | outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) |
| 316 | 316 | ); | |
| 317 | // get mappings for the inner class | 317 | } |
| 318 | ClassMapping innerClassMapping = null; | 318 | mappingsChain.add(innerClassMapping); |
| 319 | if (outerClassMapping != null) { | 319 | outerClassMapping = innerClassMapping; |
| 320 | innerClassMapping = this.direction.choose( | 320 | } |
| 321 | outerClassMapping.getInnerClassByObfSimple(parts[i]), | 321 | |
| 322 | outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) | 322 | assert (mappingsChain.size() == parts.length); |
| 323 | ); | 323 | return mappingsChain; |
| 324 | } | 324 | } |
| 325 | mappingsChain.add(innerClassMapping); | 325 | |
| 326 | outerClassMapping = innerClassMapping; | 326 | public Mappings.EntryModifier getModifier(Entry entry) { |
| 327 | } | 327 | ClassMapping classMapping = findClassMapping(entry.getClassEntry()); |
| 328 | 328 | if (classMapping != null && !entry.getName().equals("<clinit>")) { | |
| 329 | assert (mappingsChain.size() == parts.length); | 329 | if (entry instanceof ClassEntry) |
| 330 | return mappingsChain; | 330 | return classMapping.getModifier(); |
| 331 | } | 331 | else if (entry instanceof FieldEntry) { |
| 332 | 332 | FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); | |
| 333 | public Mappings.EntryModifier getModifier(Entry entry) | 333 | return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; |
| 334 | { | 334 | } else if (entry instanceof BehaviorEntry) { |
| 335 | ClassMapping classMapping = findClassMapping(entry.getClassEntry()); | 335 | MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); |
| 336 | if (classMapping != null && !entry.getName().equals("<clinit>")) | 336 | return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; |
| 337 | { | 337 | } else |
| 338 | if (entry instanceof ClassEntry) | 338 | throw new Error("Unknown entry type: " + entry.getClass().getName()); |
| 339 | return classMapping.getModifier(); | 339 | } |
| 340 | else if (entry instanceof FieldEntry) | 340 | return Mappings.EntryModifier.UNCHANGED; |
| 341 | { | 341 | } |
| 342 | FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); | ||
| 343 | return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; | ||
| 344 | } | ||
| 345 | else if (entry instanceof BehaviorEntry) | ||
| 346 | { | ||
| 347 | MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); | ||
| 348 | return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; | ||
| 349 | } | ||
| 350 | else | ||
| 351 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | ||
| 352 | } | ||
| 353 | return Mappings.EntryModifier.UNCHANGED; | ||
| 354 | } | ||
| 355 | } | 342 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/Type.java index 8136e13b..609bd64e 100644 --- a/src/main/java/cuchaz/enigma/mapping/Type.java +++ b/src/main/java/cuchaz/enigma/mapping/Type.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.mapping; | 12 | package cuchaz.enigma.mapping; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| @@ -16,219 +17,219 @@ import java.util.Map; | |||
| 16 | 17 | ||
| 17 | public class Type { | 18 | public class Type { |
| 18 | 19 | ||
| 19 | public enum Primitive { | 20 | protected String name; |
| 20 | Byte('B'), | 21 | |
| 21 | Character('C'), | 22 | public Type(String name) { |
| 22 | Short('S'), | 23 | |
| 23 | Integer('I'), | 24 | // don't deal with generics |
| 24 | Long('J'), | 25 | // this is just for raw jvm types |
| 25 | Float('F'), | 26 | if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { |
| 26 | Double('D'), | 27 | throw new IllegalArgumentException("don't use with generic types or templates: " + name); |
| 27 | Boolean('Z'); | 28 | } |
| 28 | 29 | ||
| 29 | private static final Map<Character, Primitive> lookup; | 30 | this.name = name; |
| 30 | 31 | } | |
| 31 | static { | 32 | |
| 32 | lookup = Maps.newTreeMap(); | 33 | public Type(Type other, ClassNameReplacer replacer) { |
| 33 | for (Primitive val : values()) { | 34 | this.name = other.name; |
| 34 | lookup.put(val.getCode(), val); | 35 | if (other.isClass()) { |
| 35 | } | 36 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 36 | } | 37 | if (replacedName != null) { |
| 37 | 38 | this.name = "L" + replacedName + ";"; | |
| 38 | public static Primitive get(char code) { | 39 | } |
| 39 | return lookup.get(code); | 40 | } else if (other.isArray() && other.hasClass()) { |
| 40 | } | 41 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 41 | 42 | if (replacedName != null) { | |
| 42 | private char code; | 43 | this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; |
| 43 | 44 | } | |
| 44 | Primitive(char code) { | 45 | } |
| 45 | this.code = code; | 46 | } |
| 46 | } | 47 | |
| 47 | 48 | public static String parseFirst(String in) { | |
| 48 | public char getCode() { | 49 | |
| 49 | return this.code; | 50 | if (in == null || in.length() <= 0) { |
| 50 | } | 51 | throw new IllegalArgumentException("No type to parse, input is empty!"); |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | public static String parseFirst(String in) { | 54 | // read one type from the input |
| 54 | 55 | ||
| 55 | if (in == null || in.length() <= 0) { | 56 | char c = in.charAt(0); |
| 56 | throw new IllegalArgumentException("No type to parse, input is empty!"); | 57 | |
| 57 | } | 58 | // first check for void |
| 58 | 59 | if (c == 'V') { | |
| 59 | // read one type from the input | 60 | return "V"; |
| 60 | 61 | } | |
| 61 | char c = in.charAt(0); | 62 | |
| 62 | 63 | // then check for primitives | |
| 63 | // first check for void | 64 | Primitive primitive = Primitive.get(c); |
| 64 | if (c == 'V') { | 65 | if (primitive != null) { |
| 65 | return "V"; | 66 | return in.substring(0, 1); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | // then check for primitives | 69 | // then check for classes |
| 69 | Primitive primitive = Primitive.get(c); | 70 | if (c == 'L') { |
| 70 | if (primitive != null) { | 71 | return readClass(in); |
| 71 | return in.substring(0, 1); | 72 | } |
| 72 | } | 73 | |
| 73 | 74 | // then check for templates | |
| 74 | // then check for classes | 75 | if (c == 'T') { |
| 75 | if (c == 'L') { | 76 | return readClass(in); |
| 76 | return readClass(in); | 77 | } |
| 77 | } | 78 | |
| 78 | 79 | // then check for arrays | |
| 79 | // then check for templates | 80 | int dim = countArrayDimension(in); |
| 80 | if (c == 'T') { | 81 | if (dim > 0) { |
| 81 | return readClass(in); | 82 | String arrayType = Type.parseFirst(in.substring(dim)); |
| 82 | } | 83 | return in.substring(0, dim + arrayType.length()); |
| 83 | 84 | } | |
| 84 | // then check for arrays | 85 | |
| 85 | int dim = countArrayDimension(in); | 86 | throw new IllegalArgumentException("don't know how to parse: " + in); |
| 86 | if (dim > 0) { | 87 | } |
| 87 | String arrayType = Type.parseFirst(in.substring(dim)); | 88 | |
| 88 | return in.substring(0, dim + arrayType.length()); | 89 | private static String getArrayPrefix(int dimension) { |
| 89 | } | 90 | StringBuilder buf = new StringBuilder(); |
| 90 | 91 | for (int i = 0; i < dimension; i++) { | |
| 91 | throw new IllegalArgumentException("don't know how to parse: " + in); | 92 | buf.append("["); |
| 92 | } | 93 | } |
| 93 | 94 | return buf.toString(); | |
| 94 | protected String name; | 95 | } |
| 95 | 96 | ||
| 96 | public Type(String name) { | 97 | private static int countArrayDimension(String in) { |
| 97 | 98 | int i = 0; | |
| 98 | // don't deal with generics | 99 | while (i < in.length() && in.charAt(i) == '[') |
| 99 | // this is just for raw jvm types | 100 | i++; |
| 100 | if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { | 101 | return i; |
| 101 | throw new IllegalArgumentException("don't use with generic types or templates: " + name); | 102 | } |
| 102 | } | 103 | |
| 103 | 104 | private static String readClass(String in) { | |
| 104 | this.name = name; | 105 | // read all the characters in the buffer until we hit a ';' |
| 105 | } | 106 | // include the parameters too |
| 106 | 107 | StringBuilder buf = new StringBuilder(); | |
| 107 | public Type(Type other, ClassNameReplacer replacer) { | 108 | int depth = 0; |
| 108 | this.name = other.name; | 109 | for (int i = 0; i < in.length(); i++) { |
| 109 | if (other.isClass()) { | 110 | char c = in.charAt(i); |
| 110 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); | 111 | buf.append(c); |
| 111 | if (replacedName != null) { | 112 | |
| 112 | this.name = "L" + replacedName + ";"; | 113 | if (c == '<') { |
| 113 | } | 114 | depth++; |
| 114 | } else if (other.isArray() && other.hasClass()) { | 115 | } else if (c == '>') { |
| 115 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); | 116 | depth--; |
| 116 | if (replacedName != null) { | 117 | } else if (depth == 0 && c == ';') { |
| 117 | this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; | 118 | return buf.toString(); |
| 118 | } | 119 | } |
| 119 | } | 120 | } |
| 120 | } | 121 | return null; |
| 121 | 122 | } | |
| 122 | @Override | 123 | |
| 123 | public String toString() { | 124 | @Override |
| 124 | return this.name; | 125 | public String toString() { |
| 125 | } | 126 | return this.name; |
| 126 | 127 | } | |
| 127 | public boolean isVoid() { | 128 | |
| 128 | return this.name.length() == 1 && this.name.charAt(0) == 'V'; | 129 | public boolean isVoid() { |
| 129 | } | 130 | return this.name.length() == 1 && this.name.charAt(0) == 'V'; |
| 130 | 131 | } | |
| 131 | public boolean isPrimitive() { | 132 | |
| 132 | return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; | 133 | public boolean isPrimitive() { |
| 133 | } | 134 | return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; |
| 134 | 135 | } | |
| 135 | public Primitive getPrimitive() { | 136 | |
| 136 | if (!isPrimitive()) { | 137 | public Primitive getPrimitive() { |
| 137 | throw new IllegalStateException("not a primitive"); | 138 | if (!isPrimitive()) { |
| 138 | } | 139 | throw new IllegalStateException("not a primitive"); |
| 139 | return Primitive.get(this.name.charAt(0)); | 140 | } |
| 140 | } | 141 | return Primitive.get(this.name.charAt(0)); |
| 141 | 142 | } | |
| 142 | public boolean isClass() { | 143 | |
| 143 | return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; | 144 | public boolean isClass() { |
| 144 | } | 145 | return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; |
| 145 | 146 | } | |
| 146 | public ClassEntry getClassEntry() { | 147 | |
| 147 | if (isClass()) { | 148 | public ClassEntry getClassEntry() { |
| 148 | String name = this.name.substring(1, this.name.length() - 1); | 149 | if (isClass()) { |
| 149 | 150 | String name = this.name.substring(1, this.name.length() - 1); | |
| 150 | int pos = name.indexOf('<'); | 151 | |
| 151 | if (pos >= 0) { | 152 | int pos = name.indexOf('<'); |
| 152 | // remove the parameters from the class name | 153 | if (pos >= 0) { |
| 153 | name = name.substring(0, pos); | 154 | // remove the parameters from the class name |
| 154 | } | 155 | name = name.substring(0, pos); |
| 155 | 156 | } | |
| 156 | return new ClassEntry(name); | 157 | |
| 157 | 158 | return new ClassEntry(name); | |
| 158 | } else if (isArray() && getArrayType().isClass()) { | 159 | |
| 159 | return getArrayType().getClassEntry(); | 160 | } else if (isArray() && getArrayType().isClass()) { |
| 160 | } else { | 161 | return getArrayType().getClassEntry(); |
| 161 | throw new IllegalStateException("type doesn't have a class"); | 162 | } else { |
| 162 | } | 163 | throw new IllegalStateException("type doesn't have a class"); |
| 163 | } | 164 | } |
| 164 | 165 | } | |
| 165 | public boolean isArray() { | 166 | |
| 166 | return this.name.charAt(0) == '['; | 167 | public boolean isArray() { |
| 167 | } | 168 | return this.name.charAt(0) == '['; |
| 168 | 169 | } | |
| 169 | public int getArrayDimension() { | 170 | |
| 170 | if (!isArray()) { | 171 | public int getArrayDimension() { |
| 171 | throw new IllegalStateException("not an array"); | 172 | if (!isArray()) { |
| 172 | } | 173 | throw new IllegalStateException("not an array"); |
| 173 | return countArrayDimension(this.name); | 174 | } |
| 174 | } | 175 | return countArrayDimension(this.name); |
| 175 | 176 | } | |
| 176 | public Type getArrayType() { | 177 | |
| 177 | if (!isArray()) { | 178 | public Type getArrayType() { |
| 178 | throw new IllegalStateException("not an array"); | 179 | if (!isArray()) { |
| 179 | } | 180 | throw new IllegalStateException("not an array"); |
| 180 | return new Type(this.name.substring(getArrayDimension(), this.name.length())); | 181 | } |
| 181 | } | 182 | return new Type(this.name.substring(getArrayDimension(), this.name.length())); |
| 182 | 183 | } | |
| 183 | private static String getArrayPrefix(int dimension) { | 184 | |
| 184 | StringBuilder buf = new StringBuilder(); | 185 | public boolean hasClass() { |
| 185 | for (int i = 0; i < dimension; i++) { | 186 | return isClass() || (isArray() && getArrayType().hasClass()); |
| 186 | buf.append("["); | 187 | } |
| 187 | } | 188 | |
| 188 | return buf.toString(); | 189 | @Override |
| 189 | } | 190 | public boolean equals(Object other) { |
| 190 | 191 | return other instanceof Type && equals((Type) other); | |
| 191 | public boolean hasClass() { | 192 | } |
| 192 | return isClass() || (isArray() && getArrayType().hasClass()); | 193 | |
| 193 | } | 194 | public boolean equals(Type other) { |
| 194 | 195 | return this.name.equals(other.name); | |
| 195 | @Override | 196 | } |
| 196 | public boolean equals(Object other) { | 197 | |
| 197 | return other instanceof Type && equals((Type) other); | 198 | public int hashCode() { |
| 198 | } | 199 | return this.name.hashCode(); |
| 199 | 200 | } | |
| 200 | public boolean equals(Type other) { | 201 | |
| 201 | return this.name.equals(other.name); | 202 | public enum Primitive { |
| 202 | } | 203 | Byte('B'), |
| 203 | 204 | Character('C'), | |
| 204 | public int hashCode() { | 205 | Short('S'), |
| 205 | return this.name.hashCode(); | 206 | Integer('I'), |
| 206 | } | 207 | Long('J'), |
| 207 | 208 | Float('F'), | |
| 208 | private static int countArrayDimension(String in) { | 209 | Double('D'), |
| 209 | int i = 0; | 210 | Boolean('Z'); |
| 210 | while (i < in.length() && in.charAt(i) == '[') | 211 | |
| 211 | i++; | 212 | private static final Map<Character, Primitive> lookup; |
| 212 | return i; | 213 | |
| 213 | } | 214 | static { |
| 214 | 215 | lookup = Maps.newTreeMap(); | |
| 215 | private static String readClass(String in) { | 216 | for (Primitive val : values()) { |
| 216 | // read all the characters in the buffer until we hit a ';' | 217 | lookup.put(val.getCode(), val); |
| 217 | // include the parameters too | 218 | } |
| 218 | StringBuilder buf = new StringBuilder(); | 219 | } |
| 219 | int depth = 0; | 220 | |
| 220 | for (int i = 0; i < in.length(); i++) { | 221 | private char code; |
| 221 | char c = in.charAt(i); | 222 | |
| 222 | buf.append(c); | 223 | Primitive(char code) { |
| 223 | 224 | this.code = code; | |
| 224 | if (c == '<') { | 225 | } |
| 225 | depth++; | 226 | |
| 226 | } else if (c == '>') { | 227 | public static Primitive get(char code) { |
| 227 | depth--; | 228 | return lookup.get(code); |
| 228 | } else if (depth == 0 && c == ';') { | 229 | } |
| 229 | return buf.toString(); | 230 | |
| 230 | } | 231 | public char getCode() { |
| 231 | } | 232 | return this.code; |
| 232 | return null; | 233 | } |
| 233 | } | 234 | } |
| 234 | } | 235 | } |
diff --git a/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java index fa21a9e5..0155ad27 100644 --- a/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java +++ b/src/main/java/cuchaz/enigma/throwables/IllegalNameException.java | |||
| @@ -8,31 +8,32 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.throwables; | 12 | package cuchaz.enigma.throwables; |
| 12 | 13 | ||
| 13 | public class IllegalNameException extends RuntimeException { | 14 | public class IllegalNameException extends RuntimeException { |
| 14 | 15 | ||
| 15 | private String name; | 16 | private String name; |
| 16 | private String reason; | 17 | private String reason; |
| 17 | 18 | ||
| 18 | public IllegalNameException(String name, String reason) { | 19 | public IllegalNameException(String name, String reason) { |
| 19 | this.name = name; | 20 | this.name = name; |
| 20 | this.reason = reason; | 21 | this.reason = reason; |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | public String getReason() { | 24 | public String getReason() { |
| 24 | return this.reason; | 25 | return this.reason; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | @Override | 28 | @Override |
| 28 | public String getMessage() { | 29 | public String getMessage() { |
| 29 | StringBuilder buf = new StringBuilder(); | 30 | StringBuilder buf = new StringBuilder(); |
| 30 | buf.append("Illegal name: "); | 31 | buf.append("Illegal name: "); |
| 31 | buf.append(this.name); | 32 | buf.append(this.name); |
| 32 | if (this.reason != null) { | 33 | if (this.reason != null) { |
| 33 | buf.append(" because "); | 34 | buf.append(" because "); |
| 34 | buf.append(this.reason); | 35 | buf.append(this.reason); |
| 35 | } | 36 | } |
| 36 | return buf.toString(); | 37 | return buf.toString(); |
| 37 | } | 38 | } |
| 38 | } | 39 | } |
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingConflict.java b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java index 5924f32a..95cd4494 100644 --- a/src/main/java/cuchaz/enigma/throwables/MappingConflict.java +++ b/src/main/java/cuchaz/enigma/throwables/MappingConflict.java | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | package cuchaz.enigma.throwables; | 1 | package cuchaz.enigma.throwables; |
| 2 | 2 | ||
| 3 | public class MappingConflict extends Exception { | 3 | public class MappingConflict extends Exception { |
| 4 | public MappingConflict(String clazz, String name, String nameExisting) { | 4 | public MappingConflict(String clazz, String name, String nameExisting) { |
| 5 | super(String.format("Conflicting mappings found for %s. The mapping file is %s and the second is %s", clazz, name, nameExisting)); | 5 | super(String.format("Conflicting mappings found for %s. The mapping file is %s and the second is %s", clazz, name, nameExisting)); |
| 6 | } | 6 | } |
| 7 | } | 7 | } |
diff --git a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java index d4c66734..cc5f650a 100644 --- a/src/main/java/cuchaz/enigma/throwables/MappingParseException.java +++ b/src/main/java/cuchaz/enigma/throwables/MappingParseException.java | |||
| @@ -8,24 +8,25 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.throwables; | 12 | package cuchaz.enigma.throwables; |
| 12 | 13 | ||
| 13 | import java.io.File; | 14 | import java.io.File; |
| 14 | 15 | ||
| 15 | public class MappingParseException extends Exception { | 16 | public class MappingParseException extends Exception { |
| 16 | 17 | ||
| 17 | private int line; | 18 | private int line; |
| 18 | private String message; | 19 | private String message; |
| 19 | private String filePath; | 20 | private String filePath; |
| 20 | 21 | ||
| 21 | public MappingParseException(File file, int line, String message) { | 22 | public MappingParseException(File file, int line, String message) { |
| 22 | this.line = line; | 23 | this.line = line; |
| 23 | this.message = message; | 24 | this.message = message; |
| 24 | filePath = file.getAbsolutePath(); | 25 | filePath = file.getAbsolutePath(); |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | @Override | 28 | @Override |
| 28 | public String getMessage() { | 29 | public String getMessage() { |
| 29 | return "Line " + line + ": " + message + " in file " + filePath; | 30 | return "Line " + line + ": " + message + " in file " + filePath; |
| 30 | } | 31 | } |
| 31 | } | 32 | } |
diff --git a/src/main/java/cuchaz/enigma/utils/ReadableToken.java b/src/main/java/cuchaz/enigma/utils/ReadableToken.java index 008e28cf..de152fe3 100644 --- a/src/main/java/cuchaz/enigma/utils/ReadableToken.java +++ b/src/main/java/cuchaz/enigma/utils/ReadableToken.java | |||
| @@ -8,22 +8,23 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.utils; | 12 | package cuchaz.enigma.utils; |
| 12 | 13 | ||
| 13 | public class ReadableToken { | 14 | public class ReadableToken { |
| 14 | 15 | ||
| 15 | public int line; | 16 | public int line; |
| 16 | public int startColumn; | 17 | public int startColumn; |
| 17 | public int endColumn; | 18 | public int endColumn; |
| 18 | 19 | ||
| 19 | public ReadableToken(int line, int startColumn, int endColumn) { | 20 | public ReadableToken(int line, int startColumn, int endColumn) { |
| 20 | this.line = line; | 21 | this.line = line; |
| 21 | this.startColumn = startColumn; | 22 | this.startColumn = startColumn; |
| 22 | this.endColumn = endColumn; | 23 | this.endColumn = endColumn; |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | @Override | 26 | @Override |
| 26 | public String toString() { | 27 | public String toString() { |
| 27 | return "line " + line + " columns " + startColumn + "-" + endColumn; | 28 | return "line " + line + " columns " + startColumn + "-" + endColumn; |
| 28 | } | 29 | } |
| 29 | } | 30 | } |
diff --git a/src/main/java/cuchaz/enigma/utils/Utils.java b/src/main/java/cuchaz/enigma/utils/Utils.java index 474f7edf..8e502d47 100644 --- a/src/main/java/cuchaz/enigma/utils/Utils.java +++ b/src/main/java/cuchaz/enigma/utils/Utils.java | |||
| @@ -8,12 +8,13 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.utils; | 12 | package cuchaz.enigma.utils; |
| 12 | 13 | ||
| 13 | import com.google.common.io.CharStreams; | 14 | import com.google.common.io.CharStreams; |
| 14 | 15 | ||
| 15 | import java.awt.Desktop; | 16 | import javax.swing.*; |
| 16 | import java.awt.Font; | 17 | import java.awt.*; |
| 17 | import java.awt.event.MouseEvent; | 18 | import java.awt.event.MouseEvent; |
| 18 | import java.io.IOException; | 19 | import java.io.IOException; |
| 19 | import java.io.InputStream; | 20 | import java.io.InputStream; |
| @@ -23,69 +24,66 @@ import java.net.URISyntaxException; | |||
| 23 | import java.util.Arrays; | 24 | import java.util.Arrays; |
| 24 | import java.util.List; | 25 | import java.util.List; |
| 25 | 26 | ||
| 26 | import javax.swing.*; | ||
| 27 | |||
| 28 | public class Utils { | 27 | public class Utils { |
| 29 | 28 | ||
| 30 | public static int combineHashesOrdered(Object... objs) { | 29 | public static int combineHashesOrdered(Object... objs) { |
| 31 | return combineHashesOrdered(Arrays.asList(objs)); | 30 | return combineHashesOrdered(Arrays.asList(objs)); |
| 32 | } | 31 | } |
| 33 | 32 | ||
| 34 | public static int combineHashesOrdered(List<Object> objs) { | 33 | public static int combineHashesOrdered(List<Object> objs) { |
| 35 | final int prime = 67; | 34 | final int prime = 67; |
| 36 | int result = 1; | 35 | int result = 1; |
| 37 | for (Object obj : objs) { | 36 | for (Object obj : objs) { |
| 38 | result *= prime; | 37 | result *= prime; |
| 39 | if (obj != null) { | 38 | if (obj != null) { |
| 40 | result += obj.hashCode(); | 39 | result += obj.hashCode(); |
| 41 | } | 40 | } |
| 42 | } | 41 | } |
| 43 | return result; | 42 | return result; |
| 44 | } | 43 | } |
| 45 | 44 | ||
| 46 | public static String readStreamToString(InputStream in) throws IOException { | 45 | public static String readStreamToString(InputStream in) throws IOException { |
| 47 | return CharStreams.toString(new InputStreamReader(in, "UTF-8")); | 46 | return CharStreams.toString(new InputStreamReader(in, "UTF-8")); |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | public static String readResourceToString(String path) throws IOException { | 49 | public static String readResourceToString(String path) throws IOException { |
| 51 | InputStream in = Utils.class.getResourceAsStream(path); | 50 | InputStream in = Utils.class.getResourceAsStream(path); |
| 52 | if (in == null) { | 51 | if (in == null) { |
| 53 | throw new IllegalArgumentException("Resource not found! " + path); | 52 | throw new IllegalArgumentException("Resource not found! " + path); |
| 54 | } | 53 | } |
| 55 | return readStreamToString(in); | 54 | return readStreamToString(in); |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | public static void openUrl(String url) { | 57 | public static void openUrl(String url) { |
| 59 | if (Desktop.isDesktopSupported()) { | 58 | if (Desktop.isDesktopSupported()) { |
| 60 | Desktop desktop = Desktop.getDesktop(); | 59 | Desktop desktop = Desktop.getDesktop(); |
| 61 | try { | 60 | try { |
| 62 | desktop.browse(new URI(url)); | 61 | desktop.browse(new URI(url)); |
| 63 | } catch (IOException ex) { | 62 | } catch (IOException ex) { |
| 64 | throw new Error(ex); | 63 | throw new Error(ex); |
| 65 | } catch (URISyntaxException ex) { | 64 | } catch (URISyntaxException ex) { |
| 66 | throw new IllegalArgumentException(ex); | 65 | throw new IllegalArgumentException(ex); |
| 67 | } | 66 | } |
| 68 | } | 67 | } |
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | public static JLabel unboldLabel(JLabel label) { | 70 | public static JLabel unboldLabel(JLabel label) { |
| 72 | Font font = label.getFont(); | 71 | Font font = label.getFont(); |
| 73 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); | 72 | label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); |
| 74 | return label; | 73 | return label; |
| 75 | } | 74 | } |
| 76 | 75 | ||
| 77 | public static void showToolTipNow(JComponent component) { | 76 | public static void showToolTipNow(JComponent component) { |
| 78 | // HACKHACK: trick the tooltip manager into showing the tooltip right now | 77 | // HACKHACK: trick the tooltip manager into showing the tooltip right now |
| 79 | ToolTipManager manager = ToolTipManager.sharedInstance(); | 78 | ToolTipManager manager = ToolTipManager.sharedInstance(); |
| 80 | int oldDelay = manager.getInitialDelay(); | 79 | int oldDelay = manager.getInitialDelay(); |
| 81 | manager.setInitialDelay(0); | 80 | manager.setInitialDelay(0); |
| 82 | manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); | 81 | manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); |
| 83 | manager.setInitialDelay(oldDelay); | 82 | manager.setInitialDelay(oldDelay); |
| 84 | } | 83 | } |
| 85 | 84 | ||
| 86 | public static boolean getSystemPropertyAsBoolean(String property, boolean defValue) | 85 | public static boolean getSystemPropertyAsBoolean(String property, boolean defValue) { |
| 87 | { | 86 | String value = System.getProperty(property); |
| 88 | String value = System.getProperty(property); | 87 | return value == null ? defValue : Boolean.parseBoolean(value); |
| 89 | return value == null ? defValue : Boolean.parseBoolean(value); | 88 | } |
| 90 | } | ||
| 91 | } | 89 | } |
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java index 76a3d3b5..e6c1b746 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfed.java +++ b/src/test/java/cuchaz/enigma/TestDeobfed.java | |||
| @@ -4,38 +4,36 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | |||
| 14 | import static cuchaz.enigma.TestEntryFactory.*; | ||
| 15 | import static org.hamcrest.MatcherAssert.*; | ||
| 16 | import static org.hamcrest.Matchers.*; | ||
| 17 | 11 | ||
| 18 | import java.util.jar.JarFile; | 12 | package cuchaz.enigma; |
| 19 | 13 | ||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 20 | import org.junit.BeforeClass; | 15 | import org.junit.BeforeClass; |
| 21 | import org.junit.Test; | 16 | import org.junit.Test; |
| 22 | 17 | ||
| 23 | import cuchaz.enigma.analysis.JarIndex; | 18 | import java.util.jar.JarFile; |
| 24 | 19 | ||
| 20 | import static cuchaz.enigma.TestEntryFactory.newClass; | ||
| 21 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 22 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 25 | 23 | ||
| 26 | public class TestDeobfed { | 24 | public class TestDeobfed { |
| 27 | 25 | ||
| 28 | private static JarFile jar; | 26 | private static JarFile jar; |
| 29 | private static JarIndex index; | 27 | private static JarIndex index; |
| 30 | 28 | ||
| 31 | @BeforeClass | 29 | @BeforeClass |
| 32 | public static void beforeClass() | 30 | public static void beforeClass() |
| 33 | throws Exception { | 31 | throws Exception { |
| 34 | jar = new JarFile("build/test-deobf/translation.jar"); | 32 | jar = new JarFile("build/test-deobf/translation.jar"); |
| 35 | index = new JarIndex(); | 33 | index = new JarIndex(); |
| 36 | index.indexJar(jar, true); | 34 | index.indexJar(jar, true); |
| 37 | } | 35 | } |
| 38 | 36 | ||
| 39 | @Test | 37 | @Test |
| 40 | public void obfEntries() { | 38 | public void obfEntries() { |
| 41 | assertThat(index.getObfClassEntries(), containsInAnyOrder( | 39 | assertThat(index.getObfClassEntries(), containsInAnyOrder( |
| @@ -64,10 +62,10 @@ public class TestDeobfed { | |||
| 64 | newClass("i$b") | 62 | newClass("i$b") |
| 65 | )); | 63 | )); |
| 66 | } | 64 | } |
| 67 | 65 | ||
| 68 | @Test | 66 | @Test |
| 69 | public void decompile() | 67 | public void decompile() |
| 70 | throws Exception { | 68 | throws Exception { |
| 71 | Deobfuscator deobfuscator = new Deobfuscator(jar); | 69 | Deobfuscator deobfuscator = new Deobfuscator(jar); |
| 72 | deobfuscator.getSourceTree("a"); | 70 | deobfuscator.getSourceTree("a"); |
| 73 | deobfuscator.getSourceTree("b"); | 71 | deobfuscator.getSourceTree("b"); |
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java index 8c97ff34..62a52861 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java +++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java | |||
| @@ -4,40 +4,39 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import static org.junit.Assert.*; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 16 | import org.junit.Test; | ||
| 14 | 17 | ||
| 15 | import java.io.IOException; | 18 | import java.io.IOException; |
| 16 | import java.util.List; | 19 | import java.util.List; |
| 17 | import java.util.jar.JarFile; | 20 | import java.util.jar.JarFile; |
| 18 | 21 | ||
| 19 | import org.junit.Test; | 22 | import static org.junit.Assert.assertEquals; |
| 20 | |||
| 21 | import com.google.common.collect.Lists; | ||
| 22 | |||
| 23 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 24 | 23 | ||
| 25 | public class TestDeobfuscator { | 24 | public class TestDeobfuscator { |
| 26 | 25 | ||
| 27 | private Deobfuscator getDeobfuscator() | 26 | private Deobfuscator getDeobfuscator() |
| 28 | throws IOException { | 27 | throws IOException { |
| 29 | return new Deobfuscator(new JarFile("build/test-obf/loneClass.jar")); | 28 | return new Deobfuscator(new JarFile("build/test-obf/loneClass.jar")); |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | @Test | 31 | @Test |
| 33 | public void loadJar() | 32 | public void loadJar() |
| 34 | throws Exception { | 33 | throws Exception { |
| 35 | getDeobfuscator(); | 34 | getDeobfuscator(); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | @Test | 37 | @Test |
| 39 | public void getClasses() | 38 | public void getClasses() |
| 40 | throws Exception { | 39 | throws Exception { |
| 41 | Deobfuscator deobfuscator = getDeobfuscator(); | 40 | Deobfuscator deobfuscator = getDeobfuscator(); |
| 42 | List<ClassEntry> obfClasses = Lists.newArrayList(); | 41 | List<ClassEntry> obfClasses = Lists.newArrayList(); |
| 43 | List<ClassEntry> deobfClasses = Lists.newArrayList(); | 42 | List<ClassEntry> deobfClasses = Lists.newArrayList(); |
| @@ -47,10 +46,10 @@ public class TestDeobfuscator { | |||
| 47 | assertEquals(1, deobfClasses.size()); | 46 | assertEquals(1, deobfClasses.size()); |
| 48 | assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); | 47 | assertEquals("cuchaz/enigma/inputs/Keep", deobfClasses.get(0).getName()); |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | @Test | 50 | @Test |
| 52 | public void decompileClass() | 51 | public void decompileClass() |
| 53 | throws Exception { | 52 | throws Exception { |
| 54 | Deobfuscator deobfuscator = getDeobfuscator(); | 53 | Deobfuscator deobfuscator = getDeobfuscator(); |
| 55 | deobfuscator.getSource(deobfuscator.getSourceTree("a")); | 54 | deobfuscator.getSource(deobfuscator.getSourceTree("a")); |
| 56 | } | 55 | } |
diff --git a/src/test/java/cuchaz/enigma/TestEntryFactory.java b/src/test/java/cuchaz/enigma/TestEntryFactory.java index 4aa773b6..1c527f53 100644 --- a/src/test/java/cuchaz/enigma/TestEntryFactory.java +++ b/src/test/java/cuchaz/enigma/TestEntryFactory.java | |||
| @@ -4,64 +4,59 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import cuchaz.enigma.analysis.EntryReference; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 14 | import cuchaz.enigma.mapping.BehaviorEntry; | 15 | import cuchaz.enigma.mapping.*; |
| 15 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 16 | import cuchaz.enigma.mapping.ConstructorEntry; | ||
| 17 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 18 | import cuchaz.enigma.mapping.MethodEntry; | ||
| 19 | import cuchaz.enigma.mapping.Signature; | ||
| 20 | import cuchaz.enigma.mapping.Type; | ||
| 21 | 16 | ||
| 22 | public class TestEntryFactory { | 17 | public class TestEntryFactory { |
| 23 | 18 | ||
| 24 | public static ClassEntry newClass(String name) { | 19 | public static ClassEntry newClass(String name) { |
| 25 | return new ClassEntry(name); | 20 | return new ClassEntry(name); |
| 26 | } | 21 | } |
| 27 | 22 | ||
| 28 | public static FieldEntry newField(String className, String fieldName, String fieldType) { | 23 | public static FieldEntry newField(String className, String fieldName, String fieldType) { |
| 29 | return newField(newClass(className), fieldName, fieldType); | 24 | return newField(newClass(className), fieldName, fieldType); |
| 30 | } | 25 | } |
| 31 | 26 | ||
| 32 | public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { | 27 | public static FieldEntry newField(ClassEntry classEntry, String fieldName, String fieldType) { |
| 33 | return new FieldEntry(classEntry, fieldName, new Type(fieldType)); | 28 | return new FieldEntry(classEntry, fieldName, new Type(fieldType)); |
| 34 | } | 29 | } |
| 35 | 30 | ||
| 36 | public static MethodEntry newMethod(String className, String methodName, String methodSignature) { | 31 | public static MethodEntry newMethod(String className, String methodName, String methodSignature) { |
| 37 | return newMethod(newClass(className), methodName, methodSignature); | 32 | return newMethod(newClass(className), methodName, methodSignature); |
| 38 | } | 33 | } |
| 39 | 34 | ||
| 40 | public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { | 35 | public static MethodEntry newMethod(ClassEntry classEntry, String methodName, String methodSignature) { |
| 41 | return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); | 36 | return new MethodEntry(classEntry, methodName, new Signature(methodSignature)); |
| 42 | } | 37 | } |
| 43 | 38 | ||
| 44 | public static ConstructorEntry newConstructor(String className, String signature) { | 39 | public static ConstructorEntry newConstructor(String className, String signature) { |
| 45 | return newConstructor(newClass(className), signature); | 40 | return newConstructor(newClass(className), signature); |
| 46 | } | 41 | } |
| 47 | 42 | ||
| 48 | public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { | 43 | public static ConstructorEntry newConstructor(ClassEntry classEntry, String signature) { |
| 49 | return new ConstructorEntry(classEntry, new Signature(signature)); | 44 | return new ConstructorEntry(classEntry, new Signature(signature)); |
| 50 | } | 45 | } |
| 51 | 46 | ||
| 52 | public static EntryReference<FieldEntry,BehaviorEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { | 47 | public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByMethod(FieldEntry fieldEntry, String callerClassName, String callerName, String callerSignature) { |
| 53 | return new EntryReference<FieldEntry,BehaviorEntry>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); | 48 | return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newMethod(callerClassName, callerName, callerSignature)); |
| 54 | } | 49 | } |
| 55 | 50 | ||
| 56 | public static EntryReference<FieldEntry,BehaviorEntry> newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { | 51 | public static EntryReference<FieldEntry, BehaviorEntry> newFieldReferenceByConstructor(FieldEntry fieldEntry, String callerClassName, String callerSignature) { |
| 57 | return new EntryReference<FieldEntry,BehaviorEntry>(fieldEntry, "", newConstructor(callerClassName, callerSignature)); | 52 | return new EntryReference<FieldEntry, BehaviorEntry>(fieldEntry, "", newConstructor(callerClassName, callerSignature)); |
| 58 | } | 53 | } |
| 59 | 54 | ||
| 60 | public static EntryReference<BehaviorEntry,BehaviorEntry> newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { | 55 | public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByMethod(BehaviorEntry behaviorEntry, String callerClassName, String callerName, String callerSignature) { |
| 61 | return new EntryReference<BehaviorEntry,BehaviorEntry>(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); | 56 | return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newMethod(callerClassName, callerName, callerSignature)); |
| 62 | } | 57 | } |
| 63 | 58 | ||
| 64 | public static EntryReference<BehaviorEntry,BehaviorEntry> newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { | 59 | public static EntryReference<BehaviorEntry, BehaviorEntry> newBehaviorReferenceByConstructor(BehaviorEntry behaviorEntry, String callerClassName, String callerSignature) { |
| 65 | return new EntryReference<BehaviorEntry,BehaviorEntry>(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); | 60 | return new EntryReference<BehaviorEntry, BehaviorEntry>(behaviorEntry, "", newConstructor(callerClassName, callerSignature)); |
| 66 | } | 61 | } |
| 67 | } | 62 | } |
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java index 64a695a9..38db0df9 100644 --- a/src/test/java/cuchaz/enigma/TestInnerClasses.java +++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -4,29 +4,28 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import static org.hamcrest.MatcherAssert.*; | ||
| 14 | import static org.hamcrest.Matchers.*; | ||
| 15 | 11 | ||
| 16 | import java.util.jar.JarFile; | 12 | package cuchaz.enigma; |
| 17 | 13 | ||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 18 | import org.junit.Test; | 16 | import org.junit.Test; |
| 19 | 17 | ||
| 20 | import static cuchaz.enigma.TestEntryFactory.*; | 18 | import java.util.jar.JarFile; |
| 21 | 19 | ||
| 22 | import cuchaz.enigma.analysis.JarIndex; | 20 | import static cuchaz.enigma.TestEntryFactory.newClass; |
| 23 | import cuchaz.enigma.mapping.ClassEntry; | 21 | import static org.hamcrest.MatcherAssert.assertThat; |
| 22 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 23 | import static org.hamcrest.Matchers.empty; | ||
| 24 | import static org.hamcrest.Matchers.is; | ||
| 25 | import static org.hamcrest.Matchers.nullValue; | ||
| 24 | 26 | ||
| 25 | public class TestInnerClasses { | 27 | public class TestInnerClasses { |
| 26 | 28 | ||
| 27 | private JarIndex index; | ||
| 28 | private Deobfuscator deobfuscator; | ||
| 29 | |||
| 30 | private static final ClassEntry AnonymousOuter = newClass("a"); | 29 | private static final ClassEntry AnonymousOuter = newClass("a"); |
| 31 | private static final ClassEntry AnonymousInner = newClass("a$1"); | 30 | private static final ClassEntry AnonymousInner = newClass("a$1"); |
| 32 | private static final ClassEntry SimpleOuter = newClass("d"); | 31 | private static final ClassEntry SimpleOuter = newClass("d"); |
| @@ -41,15 +40,17 @@ public class TestInnerClasses { | |||
| 41 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); | 40 | private static final ClassEntry ClassTreeLevel1 = newClass("f$a"); |
| 42 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); | 41 | private static final ClassEntry ClassTreeLevel2 = newClass("f$a$a"); |
| 43 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); | 42 | private static final ClassEntry ClassTreeLevel3 = newClass("f$a$a$a"); |
| 44 | 43 | private JarIndex index; | |
| 44 | private Deobfuscator deobfuscator; | ||
| 45 | |||
| 45 | public TestInnerClasses() | 46 | public TestInnerClasses() |
| 46 | throws Exception { | 47 | throws Exception { |
| 47 | index = new JarIndex(); | 48 | index = new JarIndex(); |
| 48 | JarFile jar = new JarFile("build/test-obf/innerClasses.jar"); | 49 | JarFile jar = new JarFile("build/test-obf/innerClasses.jar"); |
| 49 | index.indexJar(jar, true); | 50 | index.indexJar(jar, true); |
| 50 | deobfuscator = new Deobfuscator(jar); | 51 | deobfuscator = new Deobfuscator(jar); |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | @Test | 54 | @Test |
| 54 | public void simple() { | 55 | public void simple() { |
| 55 | assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter)); | 56 | assertThat(index.getOuterClass(SimpleInner), is(SimpleOuter)); |
| @@ -57,7 +58,7 @@ public class TestInnerClasses { | |||
| 57 | assertThat(index.isAnonymousClass(SimpleInner), is(false)); | 58 | assertThat(index.isAnonymousClass(SimpleInner), is(false)); |
| 58 | decompile(SimpleOuter); | 59 | decompile(SimpleOuter); |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | @Test | 62 | @Test |
| 62 | public void anonymous() { | 63 | public void anonymous() { |
| 63 | assertThat(index.getOuterClass(AnonymousInner), is(AnonymousOuter)); | 64 | assertThat(index.getOuterClass(AnonymousInner), is(AnonymousOuter)); |
| @@ -65,7 +66,7 @@ public class TestInnerClasses { | |||
| 65 | assertThat(index.isAnonymousClass(AnonymousInner), is(true)); | 66 | assertThat(index.isAnonymousClass(AnonymousInner), is(true)); |
| 66 | decompile(AnonymousOuter); | 67 | decompile(AnonymousOuter); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | @Test | 70 | @Test |
| 70 | public void constructorArgs() { | 71 | public void constructorArgs() { |
| 71 | assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); | 72 | assertThat(index.getOuterClass(ConstructorArgsInner), is(ConstructorArgsOuter)); |
| @@ -73,7 +74,7 @@ public class TestInnerClasses { | |||
| 73 | assertThat(index.isAnonymousClass(ConstructorArgsInner), is(false)); | 74 | assertThat(index.isAnonymousClass(ConstructorArgsInner), is(false)); |
| 74 | decompile(ConstructorArgsOuter); | 75 | decompile(ConstructorArgsOuter); |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | @Test | 78 | @Test |
| 78 | public void anonymousWithScopeArgs() { | 79 | public void anonymousWithScopeArgs() { |
| 79 | assertThat(index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); | 80 | assertThat(index.getOuterClass(AnonymousWithScopeArgsInner), is(AnonymousWithScopeArgsOuter)); |
| @@ -81,7 +82,7 @@ public class TestInnerClasses { | |||
| 81 | assertThat(index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); | 82 | assertThat(index.isAnonymousClass(AnonymousWithScopeArgsInner), is(true)); |
| 82 | decompile(AnonymousWithScopeArgsOuter); | 83 | decompile(AnonymousWithScopeArgsOuter); |
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | @Test | 86 | @Test |
| 86 | public void anonymousWithOuterAccess() { | 87 | public void anonymousWithOuterAccess() { |
| 87 | assertThat(index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); | 88 | assertThat(index.getOuterClass(AnonymousWithOuterAccessInner), is(AnonymousWithOuterAccessOuter)); |
| @@ -89,15 +90,15 @@ public class TestInnerClasses { | |||
| 89 | assertThat(index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); | 90 | assertThat(index.isAnonymousClass(AnonymousWithOuterAccessInner), is(true)); |
| 90 | decompile(AnonymousWithOuterAccessOuter); | 91 | decompile(AnonymousWithOuterAccessOuter); |
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | @Test | 94 | @Test |
| 94 | public void classTree() { | 95 | public void classTree() { |
| 95 | 96 | ||
| 96 | // root level | 97 | // root level |
| 97 | assertThat(index.containsObfClass(ClassTreeRoot), is(true)); | 98 | assertThat(index.containsObfClass(ClassTreeRoot), is(true)); |
| 98 | assertThat(index.getOuterClass(ClassTreeRoot), is(nullValue())); | 99 | assertThat(index.getOuterClass(ClassTreeRoot), is(nullValue())); |
| 99 | assertThat(index.getInnerClasses(ClassTreeRoot), containsInAnyOrder(ClassTreeLevel1)); | 100 | assertThat(index.getInnerClasses(ClassTreeRoot), containsInAnyOrder(ClassTreeLevel1)); |
| 100 | 101 | ||
| 101 | // level 1 | 102 | // level 1 |
| 102 | ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() | 103 | ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() |
| 103 | + "$" + ClassTreeLevel1.getInnermostClassName() | 104 | + "$" + ClassTreeLevel1.getInnermostClassName() |
| @@ -105,7 +106,7 @@ public class TestInnerClasses { | |||
| 105 | assertThat(index.containsObfClass(fullClassEntry), is(true)); | 106 | assertThat(index.containsObfClass(fullClassEntry), is(true)); |
| 106 | assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot)); | 107 | assertThat(index.getOuterClass(ClassTreeLevel1), is(ClassTreeRoot)); |
| 107 | assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2)); | 108 | assertThat(index.getInnerClasses(ClassTreeLevel1), containsInAnyOrder(ClassTreeLevel2)); |
| 108 | 109 | ||
| 109 | // level 2 | 110 | // level 2 |
| 110 | fullClassEntry = new ClassEntry(ClassTreeRoot.getName() | 111 | fullClassEntry = new ClassEntry(ClassTreeRoot.getName() |
| 111 | + "$" + ClassTreeLevel1.getInnermostClassName() | 112 | + "$" + ClassTreeLevel1.getInnermostClassName() |
| @@ -114,7 +115,7 @@ public class TestInnerClasses { | |||
| 114 | assertThat(index.containsObfClass(fullClassEntry), is(true)); | 115 | assertThat(index.containsObfClass(fullClassEntry), is(true)); |
| 115 | assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1)); | 116 | assertThat(index.getOuterClass(ClassTreeLevel2), is(ClassTreeLevel1)); |
| 116 | assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3)); | 117 | assertThat(index.getInnerClasses(ClassTreeLevel2), containsInAnyOrder(ClassTreeLevel3)); |
| 117 | 118 | ||
| 118 | // level 3 | 119 | // level 3 |
| 119 | fullClassEntry = new ClassEntry(ClassTreeRoot.getName() | 120 | fullClassEntry = new ClassEntry(ClassTreeRoot.getName() |
| 120 | + "$" + ClassTreeLevel1.getInnermostClassName() | 121 | + "$" + ClassTreeLevel1.getInnermostClassName() |
| @@ -125,7 +126,7 @@ public class TestInnerClasses { | |||
| 125 | assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2)); | 126 | assertThat(index.getOuterClass(ClassTreeLevel3), is(ClassTreeLevel2)); |
| 126 | assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty())); | 127 | assertThat(index.getInnerClasses(ClassTreeLevel3), is(empty())); |
| 127 | } | 128 | } |
| 128 | 129 | ||
| 129 | private void decompile(ClassEntry classEntry) { | 130 | private void decompile(ClassEntry classEntry) { |
| 130 | deobfuscator.getSourceTree(classEntry.getName()); | 131 | deobfuscator.getSourceTree(classEntry.getName()); |
| 131 | } | 132 | } |
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java index 01d4bab6..edb859a7 100644 --- a/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java | |||
| @@ -4,62 +4,67 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import static cuchaz.enigma.TestEntryFactory.*; | 14 | import cuchaz.enigma.analysis.EntryReference; |
| 14 | import static org.hamcrest.MatcherAssert.*; | 15 | import cuchaz.enigma.analysis.JarIndex; |
| 15 | import static org.hamcrest.Matchers.*; | 16 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 17 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 18 | import org.junit.Test; | ||
| 16 | 19 | ||
| 17 | import java.io.File; | 20 | import java.io.File; |
| 18 | import java.util.Collection; | 21 | import java.util.Collection; |
| 19 | import java.util.jar.JarFile; | 22 | import java.util.jar.JarFile; |
| 20 | 23 | ||
| 21 | import org.junit.Test; | 24 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor; |
| 22 | 25 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; | |
| 23 | import cuchaz.enigma.analysis.EntryReference; | 26 | import static cuchaz.enigma.TestEntryFactory.newClass; |
| 24 | import cuchaz.enigma.analysis.JarIndex; | 27 | import static cuchaz.enigma.TestEntryFactory.newConstructor; |
| 25 | import cuchaz.enigma.mapping.BehaviorEntry; | 28 | import static org.hamcrest.MatcherAssert.assertThat; |
| 26 | import cuchaz.enigma.mapping.ClassEntry; | 29 | import static org.hamcrest.Matchers.containsInAnyOrder; |
| 30 | import static org.hamcrest.Matchers.empty; | ||
| 31 | import static org.hamcrest.Matchers.is; | ||
| 27 | 32 | ||
| 28 | public class TestJarIndexConstructorReferences { | 33 | public class TestJarIndexConstructorReferences { |
| 29 | 34 | ||
| 30 | private JarIndex index; | 35 | private JarIndex index; |
| 31 | 36 | ||
| 32 | private ClassEntry baseClass = newClass("a"); | 37 | private ClassEntry baseClass = newClass("a"); |
| 33 | private ClassEntry subClass = newClass("d"); | 38 | private ClassEntry subClass = newClass("d"); |
| 34 | private ClassEntry subsubClass = newClass("e"); | 39 | private ClassEntry subsubClass = newClass("e"); |
| 35 | private ClassEntry defaultClass = newClass("c"); | 40 | private ClassEntry defaultClass = newClass("c"); |
| 36 | private ClassEntry callerClass = newClass("b"); | 41 | private ClassEntry callerClass = newClass("b"); |
| 37 | 42 | ||
| 38 | public TestJarIndexConstructorReferences() | 43 | public TestJarIndexConstructorReferences() |
| 39 | throws Exception { | 44 | throws Exception { |
| 40 | File jarFile = new File("build/test-obf/constructors.jar"); | 45 | File jarFile = new File("build/test-obf/constructors.jar"); |
| 41 | index = new JarIndex(); | 46 | index = new JarIndex(); |
| 42 | index.indexJar(new JarFile(jarFile), false); | 47 | index.indexJar(new JarFile(jarFile), false); |
| 43 | } | 48 | } |
| 44 | 49 | ||
| 45 | @Test | 50 | @Test |
| 46 | public void obfEntries() { | 51 | public void obfEntries() { |
| 47 | assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, | 52 | assertThat(index.getObfClassEntries(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, |
| 48 | subClass, subsubClass, defaultClass, callerClass)); | 53 | subClass, subsubClass, defaultClass, callerClass)); |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 51 | @Test | 56 | @Test |
| 52 | @SuppressWarnings("unchecked") | 57 | @SuppressWarnings("unchecked") |
| 53 | public void baseDefault() { | 58 | public void baseDefault() { |
| 54 | BehaviorEntry source = newConstructor(baseClass, "()V"); | 59 | BehaviorEntry source = newConstructor(baseClass, "()V"); |
| 55 | Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references = index.getBehaviorReferences(source); | 60 | Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references = index.getBehaviorReferences(source); |
| 56 | assertThat(references, containsInAnyOrder( | 61 | assertThat(references, containsInAnyOrder( |
| 57 | newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), | 62 | newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), |
| 58 | newBehaviorReferenceByConstructor(source, subClass.getName(), "()V"), | 63 | newBehaviorReferenceByConstructor(source, subClass.getName(), "()V"), |
| 59 | newBehaviorReferenceByConstructor(source, subClass.getName(), "(III)V") | 64 | newBehaviorReferenceByConstructor(source, subClass.getName(), "(III)V") |
| 60 | )); | 65 | )); |
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | @Test | 68 | @Test |
| 64 | @SuppressWarnings("unchecked") | 69 | @SuppressWarnings("unchecked") |
| 65 | public void baseInt() { | 70 | public void baseInt() { |
| @@ -68,7 +73,7 @@ public class TestJarIndexConstructorReferences { | |||
| 68 | newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") | 73 | newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") |
| 69 | )); | 74 | )); |
| 70 | } | 75 | } |
| 71 | 76 | ||
| 72 | @Test | 77 | @Test |
| 73 | @SuppressWarnings("unchecked") | 78 | @SuppressWarnings("unchecked") |
| 74 | public void subDefault() { | 79 | public void subDefault() { |
| @@ -78,7 +83,7 @@ public class TestJarIndexConstructorReferences { | |||
| 78 | newBehaviorReferenceByConstructor(source, subClass.getName(), "(I)V") | 83 | newBehaviorReferenceByConstructor(source, subClass.getName(), "(I)V") |
| 79 | )); | 84 | )); |
| 80 | } | 85 | } |
| 81 | 86 | ||
| 82 | @Test | 87 | @Test |
| 83 | @SuppressWarnings("unchecked") | 88 | @SuppressWarnings("unchecked") |
| 84 | public void subInt() { | 89 | public void subInt() { |
| @@ -89,7 +94,7 @@ public class TestJarIndexConstructorReferences { | |||
| 89 | newBehaviorReferenceByConstructor(source, subsubClass.getName(), "(I)V") | 94 | newBehaviorReferenceByConstructor(source, subsubClass.getName(), "(I)V") |
| 90 | )); | 95 | )); |
| 91 | } | 96 | } |
| 92 | 97 | ||
| 93 | @Test | 98 | @Test |
| 94 | @SuppressWarnings("unchecked") | 99 | @SuppressWarnings("unchecked") |
| 95 | public void subIntInt() { | 100 | public void subIntInt() { |
| @@ -98,13 +103,13 @@ public class TestJarIndexConstructorReferences { | |||
| 98 | newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") | 103 | newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") |
| 99 | )); | 104 | )); |
| 100 | } | 105 | } |
| 101 | 106 | ||
| 102 | @Test | 107 | @Test |
| 103 | public void subIntIntInt() { | 108 | public void subIntIntInt() { |
| 104 | BehaviorEntry source = newConstructor(subClass, "(III)V"); | 109 | BehaviorEntry source = newConstructor(subClass, "(III)V"); |
| 105 | assertThat(index.getBehaviorReferences(source), is(empty())); | 110 | assertThat(index.getBehaviorReferences(source), is(empty())); |
| 106 | } | 111 | } |
| 107 | 112 | ||
| 108 | @Test | 113 | @Test |
| 109 | @SuppressWarnings("unchecked") | 114 | @SuppressWarnings("unchecked") |
| 110 | public void subsubInt() { | 115 | public void subsubInt() { |
| @@ -113,7 +118,7 @@ public class TestJarIndexConstructorReferences { | |||
| 113 | newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") | 118 | newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") |
| 114 | )); | 119 | )); |
| 115 | } | 120 | } |
| 116 | 121 | ||
| 117 | @Test | 122 | @Test |
| 118 | @SuppressWarnings("unchecked") | 123 | @SuppressWarnings("unchecked") |
| 119 | public void defaultConstructable() { | 124 | public void defaultConstructable() { |
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java index 4d9c8dc1..62469780 100644 --- a/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java | |||
| @@ -4,31 +4,12 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor; | ||
| 14 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; | ||
| 15 | import static cuchaz.enigma.TestEntryFactory.newClass; | ||
| 16 | import static cuchaz.enigma.TestEntryFactory.newConstructor; | ||
| 17 | import static cuchaz.enigma.TestEntryFactory.newField; | ||
| 18 | import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByConstructor; | ||
| 19 | import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod; | ||
| 20 | import static cuchaz.enigma.TestEntryFactory.newMethod; | ||
| 21 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 22 | import static org.hamcrest.Matchers.contains; | ||
| 23 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 24 | import static org.hamcrest.Matchers.empty; | ||
| 25 | import static org.hamcrest.Matchers.is; | ||
| 26 | 11 | ||
| 27 | import java.util.Collection; | 12 | package cuchaz.enigma; |
| 28 | import java.util.Set; | ||
| 29 | import java.util.jar.JarFile; | ||
| 30 | |||
| 31 | import org.junit.Test; | ||
| 32 | 13 | ||
| 33 | import cuchaz.enigma.analysis.Access; | 14 | import cuchaz.enigma.analysis.Access; |
| 34 | import cuchaz.enigma.analysis.EntryReference; | 15 | import cuchaz.enigma.analysis.EntryReference; |
| @@ -38,70 +19,82 @@ import cuchaz.enigma.mapping.BehaviorEntry; | |||
| 38 | import cuchaz.enigma.mapping.ClassEntry; | 19 | import cuchaz.enigma.mapping.ClassEntry; |
| 39 | import cuchaz.enigma.mapping.FieldEntry; | 20 | import cuchaz.enigma.mapping.FieldEntry; |
| 40 | import cuchaz.enigma.mapping.MethodEntry; | 21 | import cuchaz.enigma.mapping.MethodEntry; |
| 22 | import org.junit.Test; | ||
| 23 | |||
| 24 | import java.util.Collection; | ||
| 25 | import java.util.Set; | ||
| 26 | import java.util.jar.JarFile; | ||
| 27 | |||
| 28 | import static cuchaz.enigma.TestEntryFactory.*; | ||
| 29 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 30 | import static org.hamcrest.Matchers.contains; | ||
| 31 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 32 | import static org.hamcrest.Matchers.empty; | ||
| 33 | import static org.hamcrest.Matchers.is; | ||
| 41 | 34 | ||
| 42 | public class TestJarIndexInheritanceTree { | 35 | public class TestJarIndexInheritanceTree { |
| 43 | 36 | ||
| 44 | private JarIndex index; | 37 | private JarIndex index; |
| 45 | 38 | ||
| 46 | private ClassEntry objectClass = newClass("java/lang/Object"); | 39 | private ClassEntry objectClass = newClass("java/lang/Object"); |
| 47 | private ClassEntry baseClass = newClass("a"); | 40 | private ClassEntry baseClass = newClass("a"); |
| 48 | private ClassEntry subClassA = newClass("b"); | 41 | private ClassEntry subClassA = newClass("b"); |
| 49 | private ClassEntry subClassAA = newClass("d"); | 42 | private ClassEntry subClassAA = newClass("d"); |
| 50 | private ClassEntry subClassB = newClass("c"); | 43 | private ClassEntry subClassB = newClass("c"); |
| 51 | private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;"); | 44 | private FieldEntry nameField = newField(baseClass, "a", "Ljava/lang/String;"); |
| 52 | private FieldEntry numThingsField = newField(subClassB, "a", "I"); | 45 | private FieldEntry numThingsField = newField(subClassB, "a", "I"); |
| 53 | 46 | ||
| 54 | public TestJarIndexInheritanceTree() | 47 | public TestJarIndexInheritanceTree() |
| 55 | throws Exception { | 48 | throws Exception { |
| 56 | index = new JarIndex(); | 49 | index = new JarIndex(); |
| 57 | index.indexJar(new JarFile("build/test-obf/inheritanceTree.jar"), false); | 50 | index.indexJar(new JarFile("build/test-obf/inheritanceTree.jar"), false); |
| 58 | } | 51 | } |
| 59 | 52 | ||
| 60 | @Test | 53 | @Test |
| 61 | public void obfEntries() { | 54 | public void obfEntries() { |
| 62 | assertThat(index.getObfClassEntries(), containsInAnyOrder( | 55 | assertThat(index.getObfClassEntries(), containsInAnyOrder( |
| 63 | newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB | 56 | newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB |
| 64 | )); | 57 | )); |
| 65 | } | 58 | } |
| 66 | 59 | ||
| 67 | @Test | 60 | @Test |
| 68 | public void translationIndex() { | 61 | public void translationIndex() { |
| 69 | 62 | ||
| 70 | TranslationIndex index = this.index.getTranslationIndex(); | 63 | TranslationIndex index = this.index.getTranslationIndex(); |
| 71 | 64 | ||
| 72 | // base class | 65 | // base class |
| 73 | assertThat(index.getSuperclass(baseClass), is(objectClass)); | 66 | assertThat(index.getSuperclass(baseClass), is(objectClass)); |
| 74 | assertThat(index.getAncestry(baseClass), contains(objectClass)); | 67 | assertThat(index.getAncestry(baseClass), contains(objectClass)); |
| 75 | assertThat(index.getSubclass(baseClass), containsInAnyOrder(subClassA, subClassB | 68 | assertThat(index.getSubclass(baseClass), containsInAnyOrder(subClassA, subClassB |
| 76 | )); | 69 | )); |
| 77 | 70 | ||
| 78 | // subclass a | 71 | // subclass a |
| 79 | assertThat(index.getSuperclass(subClassA), is(baseClass)); | 72 | assertThat(index.getSuperclass(subClassA), is(baseClass)); |
| 80 | assertThat(index.getAncestry(subClassA), contains(baseClass, objectClass)); | 73 | assertThat(index.getAncestry(subClassA), contains(baseClass, objectClass)); |
| 81 | assertThat(index.getSubclass(subClassA), contains(subClassAA)); | 74 | assertThat(index.getSubclass(subClassA), contains(subClassAA)); |
| 82 | 75 | ||
| 83 | // subclass aa | 76 | // subclass aa |
| 84 | assertThat(index.getSuperclass(subClassAA), is(subClassA)); | 77 | assertThat(index.getSuperclass(subClassAA), is(subClassA)); |
| 85 | assertThat(index.getAncestry(subClassAA), contains(subClassA, baseClass, objectClass)); | 78 | assertThat(index.getAncestry(subClassAA), contains(subClassA, baseClass, objectClass)); |
| 86 | assertThat(index.getSubclass(subClassAA), is(empty())); | 79 | assertThat(index.getSubclass(subClassAA), is(empty())); |
| 87 | 80 | ||
| 88 | // subclass b | 81 | // subclass b |
| 89 | assertThat(index.getSuperclass(subClassB), is(baseClass)); | 82 | assertThat(index.getSuperclass(subClassB), is(baseClass)); |
| 90 | assertThat(index.getAncestry(subClassB), contains(baseClass, objectClass)); | 83 | assertThat(index.getAncestry(subClassB), contains(baseClass, objectClass)); |
| 91 | assertThat(index.getSubclass(subClassB), is(empty())); | 84 | assertThat(index.getSubclass(subClassB), is(empty())); |
| 92 | } | 85 | } |
| 93 | 86 | ||
| 94 | @Test | 87 | @Test |
| 95 | public void access() { | 88 | public void access() { |
| 96 | assertThat(index.getAccess(nameField), is(Access.PRIVATE)); | 89 | assertThat(index.getAccess(nameField), is(Access.PRIVATE)); |
| 97 | assertThat(index.getAccess(numThingsField), is(Access.PRIVATE)); | 90 | assertThat(index.getAccess(numThingsField), is(Access.PRIVATE)); |
| 98 | } | 91 | } |
| 99 | 92 | ||
| 100 | @Test | 93 | @Test |
| 101 | public void relatedMethodImplementations() { | 94 | public void relatedMethodImplementations() { |
| 102 | 95 | ||
| 103 | Set<MethodEntry> entries; | 96 | Set<MethodEntry> entries; |
| 104 | 97 | ||
| 105 | // getName() | 98 | // getName() |
| 106 | entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;")); | 99 | entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()Ljava/lang/String;")); |
| 107 | assertThat(entries, containsInAnyOrder( | 100 | assertThat(entries, containsInAnyOrder( |
| @@ -113,7 +106,7 @@ public class TestJarIndexInheritanceTree { | |||
| 113 | newMethod(baseClass, "a", "()Ljava/lang/String;"), | 106 | newMethod(baseClass, "a", "()Ljava/lang/String;"), |
| 114 | newMethod(subClassAA, "a", "()Ljava/lang/String;") | 107 | newMethod(subClassAA, "a", "()Ljava/lang/String;") |
| 115 | )); | 108 | )); |
| 116 | 109 | ||
| 117 | // doBaseThings() | 110 | // doBaseThings() |
| 118 | entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V")); | 111 | entries = index.getRelatedMethodImplementations(newMethod(baseClass, "a", "()V")); |
| 119 | assertThat(entries, containsInAnyOrder( | 112 | assertThat(entries, containsInAnyOrder( |
| @@ -133,24 +126,24 @@ public class TestJarIndexInheritanceTree { | |||
| 133 | newMethod(subClassAA, "a", "()V"), | 126 | newMethod(subClassAA, "a", "()V"), |
| 134 | newMethod(subClassB, "a", "()V") | 127 | newMethod(subClassB, "a", "()V") |
| 135 | )); | 128 | )); |
| 136 | 129 | ||
| 137 | // doBThings | 130 | // doBThings |
| 138 | entries = index.getRelatedMethodImplementations(newMethod(subClassB, "b", "()V")); | 131 | entries = index.getRelatedMethodImplementations(newMethod(subClassB, "b", "()V")); |
| 139 | assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V"))); | 132 | assertThat(entries, containsInAnyOrder(newMethod(subClassB, "b", "()V"))); |
| 140 | } | 133 | } |
| 141 | 134 | ||
| 142 | @Test | 135 | @Test |
| 143 | @SuppressWarnings("unchecked") | 136 | @SuppressWarnings("unchecked") |
| 144 | public void fieldReferences() { | 137 | public void fieldReferences() { |
| 145 | Collection<EntryReference<FieldEntry,BehaviorEntry>> references; | 138 | Collection<EntryReference<FieldEntry, BehaviorEntry>> references; |
| 146 | 139 | ||
| 147 | // name | 140 | // name |
| 148 | references = index.getFieldReferences(nameField); | 141 | references = index.getFieldReferences(nameField); |
| 149 | assertThat(references, containsInAnyOrder( | 142 | assertThat(references, containsInAnyOrder( |
| 150 | newFieldReferenceByConstructor(nameField, baseClass.getName(), "(Ljava/lang/String;)V"), | 143 | newFieldReferenceByConstructor(nameField, baseClass.getName(), "(Ljava/lang/String;)V"), |
| 151 | newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") | 144 | newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") |
| 152 | )); | 145 | )); |
| 153 | 146 | ||
| 154 | // numThings | 147 | // numThings |
| 155 | references = index.getFieldReferences(numThingsField); | 148 | references = index.getFieldReferences(numThingsField); |
| 156 | assertThat(references, containsInAnyOrder( | 149 | assertThat(references, containsInAnyOrder( |
| @@ -158,14 +151,14 @@ public class TestJarIndexInheritanceTree { | |||
| 158 | newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") | 151 | newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") |
| 159 | )); | 152 | )); |
| 160 | } | 153 | } |
| 161 | 154 | ||
| 162 | @Test | 155 | @Test |
| 163 | @SuppressWarnings("unchecked") | 156 | @SuppressWarnings("unchecked") |
| 164 | public void behaviorReferences() { | 157 | public void behaviorReferences() { |
| 165 | 158 | ||
| 166 | BehaviorEntry source; | 159 | BehaviorEntry source; |
| 167 | Collection<EntryReference<BehaviorEntry,BehaviorEntry>> references; | 160 | Collection<EntryReference<BehaviorEntry, BehaviorEntry>> references; |
| 168 | 161 | ||
| 169 | // baseClass constructor | 162 | // baseClass constructor |
| 170 | source = newConstructor(baseClass, "(Ljava/lang/String;)V"); | 163 | source = newConstructor(baseClass, "(Ljava/lang/String;)V"); |
| 171 | references = index.getBehaviorReferences(source); | 164 | references = index.getBehaviorReferences(source); |
| @@ -173,14 +166,14 @@ public class TestJarIndexInheritanceTree { | |||
| 173 | newBehaviorReferenceByConstructor(source, subClassA.getName(), "(Ljava/lang/String;)V"), | 166 | newBehaviorReferenceByConstructor(source, subClassA.getName(), "(Ljava/lang/String;)V"), |
| 174 | newBehaviorReferenceByConstructor(source, subClassB.getName(), "()V") | 167 | newBehaviorReferenceByConstructor(source, subClassB.getName(), "()V") |
| 175 | )); | 168 | )); |
| 176 | 169 | ||
| 177 | // subClassA constructor | 170 | // subClassA constructor |
| 178 | source = newConstructor(subClassA, "(Ljava/lang/String;)V"); | 171 | source = newConstructor(subClassA, "(Ljava/lang/String;)V"); |
| 179 | references = index.getBehaviorReferences(source); | 172 | references = index.getBehaviorReferences(source); |
| 180 | assertThat(references, containsInAnyOrder( | 173 | assertThat(references, containsInAnyOrder( |
| 181 | newBehaviorReferenceByConstructor(source, subClassAA.getName(), "()V") | 174 | newBehaviorReferenceByConstructor(source, subClassAA.getName(), "()V") |
| 182 | )); | 175 | )); |
| 183 | 176 | ||
| 184 | // baseClass.getName() | 177 | // baseClass.getName() |
| 185 | source = newMethod(baseClass, "a", "()Ljava/lang/String;"); | 178 | source = newMethod(baseClass, "a", "()Ljava/lang/String;"); |
| 186 | references = index.getBehaviorReferences(source); | 179 | references = index.getBehaviorReferences(source); |
| @@ -188,7 +181,7 @@ public class TestJarIndexInheritanceTree { | |||
| 188 | newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), | 181 | newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), |
| 189 | newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") | 182 | newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") |
| 190 | )); | 183 | )); |
| 191 | 184 | ||
| 192 | // subclassAA.getName() | 185 | // subclassAA.getName() |
| 193 | source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); | 186 | source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); |
| 194 | references = index.getBehaviorReferences(source); | 187 | references = index.getBehaviorReferences(source); |
| @@ -196,38 +189,38 @@ public class TestJarIndexInheritanceTree { | |||
| 196 | newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") | 189 | newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") |
| 197 | )); | 190 | )); |
| 198 | } | 191 | } |
| 199 | 192 | ||
| 200 | @Test | 193 | @Test |
| 201 | public void containsEntries() { | 194 | public void containsEntries() { |
| 202 | 195 | ||
| 203 | // classes | 196 | // classes |
| 204 | assertThat(index.containsObfClass(baseClass), is(true)); | 197 | assertThat(index.containsObfClass(baseClass), is(true)); |
| 205 | assertThat(index.containsObfClass(subClassA), is(true)); | 198 | assertThat(index.containsObfClass(subClassA), is(true)); |
| 206 | assertThat(index.containsObfClass(subClassAA), is(true)); | 199 | assertThat(index.containsObfClass(subClassAA), is(true)); |
| 207 | assertThat(index.containsObfClass(subClassB), is(true)); | 200 | assertThat(index.containsObfClass(subClassB), is(true)); |
| 208 | 201 | ||
| 209 | // fields | 202 | // fields |
| 210 | assertThat(index.containsObfField(nameField), is(true)); | 203 | assertThat(index.containsObfField(nameField), is(true)); |
| 211 | assertThat(index.containsObfField(numThingsField), is(true)); | 204 | assertThat(index.containsObfField(numThingsField), is(true)); |
| 212 | 205 | ||
| 213 | // methods | 206 | // methods |
| 214 | // getName() | 207 | // getName() |
| 215 | assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true)); | 208 | assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()Ljava/lang/String;")), is(true)); |
| 216 | assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false)); | 209 | assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()Ljava/lang/String;")), is(false)); |
| 217 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true)); | 210 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()Ljava/lang/String;")), is(true)); |
| 218 | assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false)); | 211 | assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()Ljava/lang/String;")), is(false)); |
| 219 | 212 | ||
| 220 | // doBaseThings() | 213 | // doBaseThings() |
| 221 | assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()V")), is(true)); | 214 | assertThat(index.containsObfBehavior(newMethod(baseClass, "a", "()V")), is(true)); |
| 222 | assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()V")), is(false)); | 215 | assertThat(index.containsObfBehavior(newMethod(subClassA, "a", "()V")), is(false)); |
| 223 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()V")), is(true)); | 216 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "a", "()V")), is(true)); |
| 224 | assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()V")), is(true)); | 217 | assertThat(index.containsObfBehavior(newMethod(subClassB, "a", "()V")), is(true)); |
| 225 | 218 | ||
| 226 | // doBThings() | 219 | // doBThings() |
| 227 | assertThat(index.containsObfBehavior(newMethod(baseClass, "b", "()V")), is(false)); | 220 | assertThat(index.containsObfBehavior(newMethod(baseClass, "b", "()V")), is(false)); |
| 228 | assertThat(index.containsObfBehavior(newMethod(subClassA, "b", "()V")), is(false)); | 221 | assertThat(index.containsObfBehavior(newMethod(subClassA, "b", "()V")), is(false)); |
| 229 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "b", "()V")), is(false)); | 222 | assertThat(index.containsObfBehavior(newMethod(subClassAA, "b", "()V")), is(false)); |
| 230 | assertThat(index.containsObfBehavior(newMethod(subClassB, "b", "()V")), is(true)); | 223 | assertThat(index.containsObfBehavior(newMethod(subClassB, "b", "()V")), is(true)); |
| 231 | 224 | ||
| 232 | } | 225 | } |
| 233 | } | 226 | } |
diff --git a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java index 8efa57c6..6cab1c84 100644 --- a/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java | |||
| @@ -4,44 +4,35 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import static cuchaz.enigma.TestEntryFactory.*; | 14 | import cuchaz.enigma.analysis.*; |
| 14 | import static org.hamcrest.MatcherAssert.*; | 15 | import cuchaz.enigma.mapping.*; |
| 15 | import static org.hamcrest.Matchers.*; | 16 | import org.junit.Test; |
| 16 | 17 | ||
| 17 | import java.util.Collection; | 18 | import java.util.Collection; |
| 18 | import java.util.Set; | 19 | import java.util.Set; |
| 19 | import java.util.jar.JarFile; | 20 | import java.util.jar.JarFile; |
| 20 | 21 | ||
| 21 | import org.junit.Test; | 22 | import static cuchaz.enigma.TestEntryFactory.*; |
| 22 | 23 | import static org.hamcrest.MatcherAssert.assertThat; | |
| 23 | import cuchaz.enigma.analysis.Access; | 24 | import static org.hamcrest.Matchers.*; |
| 24 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; | ||
| 25 | import cuchaz.enigma.analysis.ClassInheritanceTreeNode; | ||
| 26 | import cuchaz.enigma.analysis.EntryReference; | ||
| 27 | import cuchaz.enigma.analysis.JarIndex; | ||
| 28 | import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | ||
| 29 | import cuchaz.enigma.mapping.BehaviorEntry; | ||
| 30 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 31 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 32 | import cuchaz.enigma.mapping.MethodEntry; | ||
| 33 | import cuchaz.enigma.mapping.Translator; | ||
| 34 | 25 | ||
| 35 | public class TestJarIndexLoneClass { | 26 | public class TestJarIndexLoneClass { |
| 36 | 27 | ||
| 37 | private JarIndex index; | 28 | private JarIndex index; |
| 38 | 29 | ||
| 39 | public TestJarIndexLoneClass() | 30 | public TestJarIndexLoneClass() |
| 40 | throws Exception { | 31 | throws Exception { |
| 41 | index = new JarIndex(); | 32 | index = new JarIndex(); |
| 42 | index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false); | 33 | index.indexJar(new JarFile("build/test-obf/loneClass.jar"), false); |
| 43 | } | 34 | } |
| 44 | 35 | ||
| 45 | @Test | 36 | @Test |
| 46 | public void obfEntries() { | 37 | public void obfEntries() { |
| 47 | assertThat(index.getObfClassEntries(), containsInAnyOrder( | 38 | assertThat(index.getObfClassEntries(), containsInAnyOrder( |
| @@ -49,7 +40,7 @@ public class TestJarIndexLoneClass { | |||
| 49 | newClass("a") | 40 | newClass("a") |
| 50 | )); | 41 | )); |
| 51 | } | 42 | } |
| 52 | 43 | ||
| 53 | @Test | 44 | @Test |
| 54 | public void translationIndex() { | 45 | public void translationIndex() { |
| 55 | assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("a")), is(new ClassEntry("java/lang/Object"))); | 46 | assertThat(index.getTranslationIndex().getSuperclass(new ClassEntry("a")), is(new ClassEntry("java/lang/Object"))); |
| @@ -59,7 +50,7 @@ public class TestJarIndexLoneClass { | |||
| 59 | assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("a")), is(empty())); | 50 | assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("a")), is(empty())); |
| 60 | assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); | 51 | assertThat(index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty())); |
| 61 | } | 52 | } |
| 62 | 53 | ||
| 63 | @Test | 54 | @Test |
| 64 | public void access() { | 55 | public void access() { |
| 65 | assertThat(index.getAccess(newField("a", "a", "Ljava/lang/String;")), is(Access.PRIVATE)); | 56 | assertThat(index.getAccess(newField("a", "a", "Ljava/lang/String;")), is(Access.PRIVATE)); |
| @@ -67,7 +58,7 @@ public class TestJarIndexLoneClass { | |||
| 67 | assertThat(index.getAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue())); | 58 | assertThat(index.getAccess(newField("a", "b", "Ljava/lang/String;")), is(nullValue())); |
| 68 | assertThat(index.getAccess(newField("a", "a", "LFoo;")), is(nullValue())); | 59 | assertThat(index.getAccess(newField("a", "a", "LFoo;")), is(nullValue())); |
| 69 | } | 60 | } |
| 70 | 61 | ||
| 71 | @Test | 62 | @Test |
| 72 | public void classInheritance() { | 63 | public void classInheritance() { |
| 73 | ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a")); | 64 | ClassInheritanceTreeNode node = index.getClassInheritance(new Translator(), newClass("a")); |
| @@ -75,7 +66,7 @@ public class TestJarIndexLoneClass { | |||
| 75 | assertThat(node.getObfClassName(), is("a")); | 66 | assertThat(node.getObfClassName(), is("a")); |
| 76 | assertThat(node.getChildCount(), is(0)); | 67 | assertThat(node.getChildCount(), is(0)); |
| 77 | } | 68 | } |
| 78 | 69 | ||
| 79 | @Test | 70 | @Test |
| 80 | public void methodInheritance() { | 71 | public void methodInheritance() { |
| 81 | MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); | 72 | MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); |
| @@ -84,19 +75,19 @@ public class TestJarIndexLoneClass { | |||
| 84 | assertThat(node.getMethodEntry(), is(source)); | 75 | assertThat(node.getMethodEntry(), is(source)); |
| 85 | assertThat(node.getChildCount(), is(0)); | 76 | assertThat(node.getChildCount(), is(0)); |
| 86 | } | 77 | } |
| 87 | 78 | ||
| 88 | @Test | 79 | @Test |
| 89 | public void classImplementations() { | 80 | public void classImplementations() { |
| 90 | ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a")); | 81 | ClassImplementationsTreeNode node = index.getClassImplementations(new Translator(), newClass("a")); |
| 91 | assertThat(node, is(nullValue())); | 82 | assertThat(node, is(nullValue())); |
| 92 | } | 83 | } |
| 93 | 84 | ||
| 94 | @Test | 85 | @Test |
| 95 | public void methodImplementations() { | 86 | public void methodImplementations() { |
| 96 | MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); | 87 | MethodEntry source = newMethod("a", "a", "()Ljava/lang/String;"); |
| 97 | assertThat(index.getMethodImplementations(new Translator(), source), is(empty())); | 88 | assertThat(index.getMethodImplementations(new Translator(), source), is(empty())); |
| 98 | } | 89 | } |
| 99 | 90 | ||
| 100 | @Test | 91 | @Test |
| 101 | public void relatedMethodImplementations() { | 92 | public void relatedMethodImplementations() { |
| 102 | Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); | 93 | Set<MethodEntry> entries = index.getRelatedMethodImplementations(newMethod("a", "a", "()Ljava/lang/String;")); |
| @@ -104,53 +95,53 @@ public class TestJarIndexLoneClass { | |||
| 104 | newMethod("a", "a", "()Ljava/lang/String;") | 95 | newMethod("a", "a", "()Ljava/lang/String;") |
| 105 | )); | 96 | )); |
| 106 | } | 97 | } |
| 107 | 98 | ||
| 108 | @Test | 99 | @Test |
| 109 | @SuppressWarnings("unchecked") | 100 | @SuppressWarnings("unchecked") |
| 110 | public void fieldReferences() { | 101 | public void fieldReferences() { |
| 111 | FieldEntry source = newField("a", "a", "Ljava/lang/String;"); | 102 | FieldEntry source = newField("a", "a", "Ljava/lang/String;"); |
| 112 | Collection<EntryReference<FieldEntry,BehaviorEntry>> references = index.getFieldReferences(source); | 103 | Collection<EntryReference<FieldEntry, BehaviorEntry>> references = index.getFieldReferences(source); |
| 113 | assertThat(references, containsInAnyOrder( | 104 | assertThat(references, containsInAnyOrder( |
| 114 | newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"), | 105 | newFieldReferenceByConstructor(source, "a", "(Ljava/lang/String;)V"), |
| 115 | newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") | 106 | newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") |
| 116 | )); | 107 | )); |
| 117 | } | 108 | } |
| 118 | 109 | ||
| 119 | @Test | 110 | @Test |
| 120 | public void behaviorReferences() { | 111 | public void behaviorReferences() { |
| 121 | assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); | 112 | assertThat(index.getBehaviorReferences(newMethod("a", "a", "()Ljava/lang/String;")), is(empty())); |
| 122 | } | 113 | } |
| 123 | 114 | ||
| 124 | @Test | 115 | @Test |
| 125 | public void innerClasses() { | 116 | public void innerClasses() { |
| 126 | assertThat(index.getInnerClasses(newClass("a")), is(empty())); | 117 | assertThat(index.getInnerClasses(newClass("a")), is(empty())); |
| 127 | } | 118 | } |
| 128 | 119 | ||
| 129 | @Test | 120 | @Test |
| 130 | public void outerClass() { | 121 | public void outerClass() { |
| 131 | assertThat(index.getOuterClass(newClass("a")), is(nullValue())); | 122 | assertThat(index.getOuterClass(newClass("a")), is(nullValue())); |
| 132 | } | 123 | } |
| 133 | 124 | ||
| 134 | @Test | 125 | @Test |
| 135 | public void isAnonymousClass() { | 126 | public void isAnonymousClass() { |
| 136 | assertThat(index.isAnonymousClass(newClass("a")), is(false)); | 127 | assertThat(index.isAnonymousClass(newClass("a")), is(false)); |
| 137 | } | 128 | } |
| 138 | 129 | ||
| 139 | @Test | 130 | @Test |
| 140 | public void interfaces() { | 131 | public void interfaces() { |
| 141 | assertThat(index.getInterfaces("a"), is(empty())); | 132 | assertThat(index.getInterfaces("a"), is(empty())); |
| 142 | } | 133 | } |
| 143 | 134 | ||
| 144 | @Test | 135 | @Test |
| 145 | public void implementingClasses() { | 136 | public void implementingClasses() { |
| 146 | assertThat(index.getImplementingClasses("a"), is(empty())); | 137 | assertThat(index.getImplementingClasses("a"), is(empty())); |
| 147 | } | 138 | } |
| 148 | 139 | ||
| 149 | @Test | 140 | @Test |
| 150 | public void isInterface() { | 141 | public void isInterface() { |
| 151 | assertThat(index.isInterface("a"), is(false)); | 142 | assertThat(index.isInterface("a"), is(false)); |
| 152 | } | 143 | } |
| 153 | 144 | ||
| 154 | @Test | 145 | @Test |
| 155 | public void testContains() { | 146 | public void testContains() { |
| 156 | assertThat(index.containsObfClass(newClass("a")), is(true)); | 147 | assertThat(index.containsObfClass(newClass("a")), is(true)); |
diff --git a/src/test/java/cuchaz/enigma/TestSignature.java b/src/test/java/cuchaz/enigma/TestSignature.java index 8537adfb..534b43ae 100644 --- a/src/test/java/cuchaz/enigma/TestSignature.java +++ b/src/test/java/cuchaz/enigma/TestSignature.java | |||
| @@ -4,31 +4,33 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import static org.hamcrest.MatcherAssert.*; | ||
| 14 | import static org.hamcrest.Matchers.*; | ||
| 15 | 11 | ||
| 16 | import org.junit.Test; | 12 | package cuchaz.enigma; |
| 17 | 13 | ||
| 18 | import cuchaz.enigma.mapping.ClassNameReplacer; | 14 | import cuchaz.enigma.mapping.ClassNameReplacer; |
| 19 | import cuchaz.enigma.mapping.Signature; | 15 | import cuchaz.enigma.mapping.Signature; |
| 20 | import cuchaz.enigma.mapping.Type; | 16 | import cuchaz.enigma.mapping.Type; |
| 17 | import org.junit.Test; | ||
| 21 | 18 | ||
| 19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 20 | import static org.hamcrest.Matchers.contains; | ||
| 21 | import static org.hamcrest.Matchers.empty; | ||
| 22 | import static org.hamcrest.Matchers.is; | ||
| 23 | import static org.hamcrest.Matchers.not; | ||
| 22 | 24 | ||
| 23 | public class TestSignature { | 25 | public class TestSignature { |
| 24 | 26 | ||
| 25 | @Test | 27 | @Test |
| 26 | public void easiest() { | 28 | public void easiest() { |
| 27 | final Signature sig = new Signature("()V"); | 29 | final Signature sig = new Signature("()V"); |
| 28 | assertThat(sig.getArgumentTypes(), is(empty())); | 30 | assertThat(sig.getArgumentTypes(), is(empty())); |
| 29 | assertThat(sig.getReturnType(), is(new Type("V"))); | 31 | assertThat(sig.getReturnType(), is(new Type("V"))); |
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | @Test | 34 | @Test |
| 33 | public void primitives() { | 35 | public void primitives() { |
| 34 | { | 36 | { |
| @@ -56,7 +58,7 @@ public class TestSignature { | |||
| 56 | assertThat(sig.getReturnType(), is(new Type("Z"))); | 58 | assertThat(sig.getReturnType(), is(new Type("Z"))); |
| 57 | } | 59 | } |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | @Test | 62 | @Test |
| 61 | public void classes() { | 63 | public void classes() { |
| 62 | { | 64 | { |
| @@ -82,7 +84,7 @@ public class TestSignature { | |||
| 82 | assertThat(sig.getReturnType(), is(new Type("LBar;"))); | 84 | assertThat(sig.getReturnType(), is(new Type("LBar;"))); |
| 83 | } | 85 | } |
| 84 | } | 86 | } |
| 85 | 87 | ||
| 86 | @Test | 88 | @Test |
| 87 | public void arrays() { | 89 | public void arrays() { |
| 88 | { | 90 | { |
| @@ -109,7 +111,7 @@ public class TestSignature { | |||
| 109 | assertThat(sig.getReturnType(), is(new Type("[D"))); | 111 | assertThat(sig.getReturnType(), is(new Type("[D"))); |
| 110 | } | 112 | } |
| 111 | } | 113 | } |
| 112 | 114 | ||
| 113 | @Test | 115 | @Test |
| 114 | public void mixed() { | 116 | public void mixed() { |
| 115 | { | 117 | { |
| @@ -131,7 +133,7 @@ public class TestSignature { | |||
| 131 | assertThat(sig.getReturnType(), is(new Type("[LFoo;"))); | 133 | assertThat(sig.getReturnType(), is(new Type("[LFoo;"))); |
| 132 | } | 134 | } |
| 133 | } | 135 | } |
| 134 | 136 | ||
| 135 | @Test | 137 | @Test |
| 136 | public void replaceClasses() { | 138 | public void replaceClasses() { |
| 137 | { | 139 | { |
| @@ -195,7 +197,7 @@ public class TestSignature { | |||
| 195 | assertThat(sig.getReturnType(), is(new Type("LCow;"))); | 197 | assertThat(sig.getReturnType(), is(new Type("LCow;"))); |
| 196 | } | 198 | } |
| 197 | } | 199 | } |
| 198 | 200 | ||
| 199 | @Test | 201 | @Test |
| 200 | public void replaceArrayClasses() { | 202 | public void replaceArrayClasses() { |
| 201 | { | 203 | { |
| @@ -217,13 +219,13 @@ public class TestSignature { | |||
| 217 | assertThat(sig.getReturnType(), is(new Type("[[[LBeer;"))); | 219 | assertThat(sig.getReturnType(), is(new Type("[[[LBeer;"))); |
| 218 | } | 220 | } |
| 219 | } | 221 | } |
| 220 | 222 | ||
| 221 | @Test | 223 | @Test |
| 222 | public void equals() { | 224 | public void equals() { |
| 223 | 225 | ||
| 224 | // base | 226 | // base |
| 225 | assertThat(new Signature("()V"), is(new Signature("()V"))); | 227 | assertThat(new Signature("()V"), is(new Signature("()V"))); |
| 226 | 228 | ||
| 227 | // arguments | 229 | // arguments |
| 228 | assertThat(new Signature("(I)V"), is(new Signature("(I)V"))); | 230 | assertThat(new Signature("(I)V"), is(new Signature("(I)V"))); |
| 229 | assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V"))); | 231 | assertThat(new Signature("(ZIZ)V"), is(new Signature("(ZIZ)V"))); |
| @@ -238,7 +240,7 @@ public class TestSignature { | |||
| 238 | assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V")))); | 240 | assertThat(new Signature("([[Z)V"), is(not(new Signature("([[LFoo;)V")))); |
| 239 | assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); | 241 | assertThat(new Signature("(LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); |
| 240 | assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); | 242 | assertThat(new Signature("([LFoo;LBar;)V"), is(not(new Signature("(LFoo;LCow;)V")))); |
| 241 | 243 | ||
| 242 | // return type | 244 | // return type |
| 243 | assertThat(new Signature("()I"), is(new Signature("()I"))); | 245 | assertThat(new Signature("()I"), is(new Signature("()I"))); |
| 244 | assertThat(new Signature("()Z"), is(new Signature("()Z"))); | 246 | assertThat(new Signature("()Z"), is(new Signature("()Z"))); |
| @@ -246,7 +248,7 @@ public class TestSignature { | |||
| 246 | assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z"))); | 248 | assertThat(new Signature("()[[[Z"), is(new Signature("()[[[Z"))); |
| 247 | assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;"))); | 249 | assertThat(new Signature("()LFoo;"), is(new Signature("()LFoo;"))); |
| 248 | assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;"))); | 250 | assertThat(new Signature("()[LFoo;"), is(new Signature("()[LFoo;"))); |
| 249 | 251 | ||
| 250 | assertThat(new Signature("()I"), is(not(new Signature("()Z")))); | 252 | assertThat(new Signature("()I"), is(not(new Signature("()Z")))); |
| 251 | assertThat(new Signature("()Z"), is(not(new Signature("()I")))); | 253 | assertThat(new Signature("()Z"), is(not(new Signature("()I")))); |
| 252 | assertThat(new Signature("()[D"), is(not(new Signature("()[J")))); | 254 | assertThat(new Signature("()[D"), is(not(new Signature("()[J")))); |
| @@ -254,7 +256,7 @@ public class TestSignature { | |||
| 254 | assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;")))); | 256 | assertThat(new Signature("()LFoo;"), is(not(new Signature("()LBar;")))); |
| 255 | assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;")))); | 257 | assertThat(new Signature("()[LFoo;"), is(not(new Signature("()[LBar;")))); |
| 256 | } | 258 | } |
| 257 | 259 | ||
| 258 | @Test | 260 | @Test |
| 259 | public void testToString() { | 261 | public void testToString() { |
| 260 | assertThat(new Signature("()V").toString(), is("()V")); | 262 | assertThat(new Signature("()V").toString(), is("()V")); |
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java index 58d9ca91..6e9e5aec 100644 --- a/src/test/java/cuchaz/enigma/TestSourceIndex.java +++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java | |||
| @@ -4,27 +4,26 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import java.io.File; | ||
| 14 | import java.util.Set; | ||
| 15 | import java.util.jar.JarFile; | ||
| 16 | 11 | ||
| 17 | import org.junit.Test; | 12 | package cuchaz.enigma; |
| 18 | 13 | ||
| 19 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 20 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 21 | |||
| 22 | import cuchaz.enigma.mapping.ClassEntry; | 16 | import cuchaz.enigma.mapping.ClassEntry; |
| 17 | import org.junit.Test; | ||
| 18 | |||
| 19 | import java.io.File; | ||
| 20 | import java.util.Set; | ||
| 21 | import java.util.jar.JarFile; | ||
| 23 | 22 | ||
| 24 | public class TestSourceIndex { | 23 | public class TestSourceIndex { |
| 25 | @Test | 24 | @Test |
| 26 | public void indexEverything() | 25 | public void indexEverything() |
| 27 | throws Exception { | 26 | throws Exception { |
| 28 | // Figure out where Minecraft is... | 27 | // Figure out where Minecraft is... |
| 29 | final String mcDir = System.getProperty("enigma.test.minecraftdir"); | 28 | final String mcDir = System.getProperty("enigma.test.minecraftdir"); |
| 30 | File mcJar = null; | 29 | File mcJar = null; |
| @@ -32,20 +31,17 @@ public class TestSourceIndex { | |||
| 32 | String osname = System.getProperty("os.name").toLowerCase(); | 31 | String osname = System.getProperty("os.name").toLowerCase(); |
| 33 | if (osname.contains("nix") || osname.contains("nux") || osname.contains("solaris")) { | 32 | if (osname.contains("nix") || osname.contains("nux") || osname.contains("solaris")) { |
| 34 | mcJar = new File(System.getProperty("user.home"), ".minecraft/versions/1.8.3/1.8.3.jar"); | 33 | mcJar = new File(System.getProperty("user.home"), ".minecraft/versions/1.8.3/1.8.3.jar"); |
| 35 | } | 34 | } else if (osname.contains("mac") || osname.contains("darwin")) { |
| 36 | else if (osname.contains("mac") || osname.contains("darwin")) { | ||
| 37 | mcJar = new File(System.getProperty("user.home"), "Library/Application Support/minecraft/versions/1.8.3/1.8.3.jar"); | 35 | mcJar = new File(System.getProperty("user.home"), "Library/Application Support/minecraft/versions/1.8.3/1.8.3.jar"); |
| 38 | } | 36 | } else if (osname.contains("win")) { |
| 39 | else if (osname.contains("win")) { | ||
| 40 | mcJar = new File(System.getenv("AppData"), ".minecraft/versions/1.8.3/1.8.3.jar"); | 37 | mcJar = new File(System.getenv("AppData"), ".minecraft/versions/1.8.3/1.8.3.jar"); |
| 41 | } | 38 | } |
| 42 | } | 39 | } else { |
| 43 | else { | ||
| 44 | mcJar = new File(mcDir, "versions/1.8.3/1.8.3.jar"); | 40 | mcJar = new File(mcDir, "versions/1.8.3/1.8.3.jar"); |
| 45 | } | 41 | } |
| 46 | 42 | ||
| 47 | Deobfuscator deobfuscator = new Deobfuscator(new JarFile(mcJar)); | 43 | Deobfuscator deobfuscator = new Deobfuscator(new JarFile(mcJar)); |
| 48 | 44 | ||
| 49 | // get all classes that aren't inner classes | 45 | // get all classes that aren't inner classes |
| 50 | Set<ClassEntry> classEntries = Sets.newHashSet(); | 46 | Set<ClassEntry> classEntries = Sets.newHashSet(); |
| 51 | for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { | 47 | for (ClassEntry obfClassEntry : deobfuscator.getJarIndex().getObfClassEntries()) { |
| @@ -53,7 +49,7 @@ public class TestSourceIndex { | |||
| 53 | classEntries.add(obfClassEntry); | 49 | classEntries.add(obfClassEntry); |
| 54 | } | 50 | } |
| 55 | } | 51 | } |
| 56 | 52 | ||
| 57 | for (ClassEntry obfClassEntry : classEntries) { | 53 | for (ClassEntry obfClassEntry : classEntries) { |
| 58 | try { | 54 | try { |
| 59 | CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); | 55 | CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); |
diff --git a/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/src/test/java/cuchaz/enigma/TestTokensConstructors.java index 890a4fd3..e40d5fdc 100644 --- a/src/test/java/cuchaz/enigma/TestTokensConstructors.java +++ b/src/test/java/cuchaz/enigma/TestTokensConstructors.java | |||
| @@ -4,35 +4,40 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 12 | 13 | ||
| 13 | import static cuchaz.enigma.TestEntryFactory.*; | 14 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 14 | import static org.hamcrest.MatcherAssert.*; | 15 | import org.junit.Test; |
| 15 | import static org.hamcrest.Matchers.*; | ||
| 16 | 16 | ||
| 17 | import java.util.jar.JarFile; | 17 | import java.util.jar.JarFile; |
| 18 | 18 | ||
| 19 | import org.junit.Test; | 19 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByConstructor; |
| 20 | 20 | import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; | |
| 21 | import cuchaz.enigma.mapping.BehaviorEntry; | 21 | import static cuchaz.enigma.TestEntryFactory.newConstructor; |
| 22 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 23 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
| 24 | import static org.hamcrest.Matchers.empty; | ||
| 25 | import static org.hamcrest.Matchers.is; | ||
| 26 | import static org.hamcrest.Matchers.nullValue; | ||
| 22 | 27 | ||
| 23 | public class TestTokensConstructors extends TokenChecker { | 28 | public class TestTokensConstructors extends TokenChecker { |
| 24 | 29 | ||
| 25 | public TestTokensConstructors() | 30 | public TestTokensConstructors() |
| 26 | throws Exception { | 31 | throws Exception { |
| 27 | super(new JarFile("build/test-obf/constructors.jar")); | 32 | super(new JarFile("build/test-obf/constructors.jar")); |
| 28 | } | 33 | } |
| 29 | 34 | ||
| 30 | @Test | 35 | @Test |
| 31 | public void baseDeclarations() { | 36 | public void baseDeclarations() { |
| 32 | assertThat(getDeclarationToken(newConstructor("a", "()V")), is("a")); | 37 | assertThat(getDeclarationToken(newConstructor("a", "()V")), is("a")); |
| 33 | assertThat(getDeclarationToken(newConstructor("a", "(I)V")), is("a")); | 38 | assertThat(getDeclarationToken(newConstructor("a", "(I)V")), is("a")); |
| 34 | } | 39 | } |
| 35 | 40 | ||
| 36 | @Test | 41 | @Test |
| 37 | public void subDeclarations() { | 42 | public void subDeclarations() { |
| 38 | assertThat(getDeclarationToken(newConstructor("d", "()V")), is("d")); | 43 | assertThat(getDeclarationToken(newConstructor("d", "()V")), is("d")); |
| @@ -40,17 +45,17 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 40 | assertThat(getDeclarationToken(newConstructor("d", "(II)V")), is("d")); | 45 | assertThat(getDeclarationToken(newConstructor("d", "(II)V")), is("d")); |
| 41 | assertThat(getDeclarationToken(newConstructor("d", "(III)V")), is("d")); | 46 | assertThat(getDeclarationToken(newConstructor("d", "(III)V")), is("d")); |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | @Test | 49 | @Test |
| 45 | public void subsubDeclarations() { | 50 | public void subsubDeclarations() { |
| 46 | assertThat(getDeclarationToken(newConstructor("e", "(I)V")), is("e")); | 51 | assertThat(getDeclarationToken(newConstructor("e", "(I)V")), is("e")); |
| 47 | } | 52 | } |
| 48 | 53 | ||
| 49 | @Test | 54 | @Test |
| 50 | public void defaultDeclarations() { | 55 | public void defaultDeclarations() { |
| 51 | assertThat(getDeclarationToken(newConstructor("c", "()V")), nullValue()); | 56 | assertThat(getDeclarationToken(newConstructor("c", "()V")), nullValue()); |
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | @Test | 59 | @Test |
| 55 | public void baseDefaultReferences() { | 60 | public void baseDefaultReferences() { |
| 56 | BehaviorEntry source = newConstructor("a", "()V"); | 61 | BehaviorEntry source = newConstructor("a", "()V"); |
| @@ -67,7 +72,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 67 | is(empty()) // implicit call, not decompiled to token | 72 | is(empty()) // implicit call, not decompiled to token |
| 68 | ); | 73 | ); |
| 69 | } | 74 | } |
| 70 | 75 | ||
| 71 | @Test | 76 | @Test |
| 72 | public void baseIntReferences() { | 77 | public void baseIntReferences() { |
| 73 | BehaviorEntry source = newConstructor("a", "(I)V"); | 78 | BehaviorEntry source = newConstructor("a", "(I)V"); |
| @@ -76,7 +81,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 76 | containsInAnyOrder("a") | 81 | containsInAnyOrder("a") |
| 77 | ); | 82 | ); |
| 78 | } | 83 | } |
| 79 | 84 | ||
| 80 | @Test | 85 | @Test |
| 81 | public void subDefaultReferences() { | 86 | public void subDefaultReferences() { |
| 82 | BehaviorEntry source = newConstructor("d", "()V"); | 87 | BehaviorEntry source = newConstructor("d", "()V"); |
| @@ -89,7 +94,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 89 | containsInAnyOrder("this") | 94 | containsInAnyOrder("this") |
| 90 | ); | 95 | ); |
| 91 | } | 96 | } |
| 92 | 97 | ||
| 93 | @Test | 98 | @Test |
| 94 | public void subIntReferences() { | 99 | public void subIntReferences() { |
| 95 | BehaviorEntry source = newConstructor("d", "(I)V"); | 100 | BehaviorEntry source = newConstructor("d", "(I)V"); |
| @@ -106,7 +111,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 106 | containsInAnyOrder("super") | 111 | containsInAnyOrder("super") |
| 107 | ); | 112 | ); |
| 108 | } | 113 | } |
| 109 | 114 | ||
| 110 | @Test | 115 | @Test |
| 111 | public void subIntIntReferences() { | 116 | public void subIntIntReferences() { |
| 112 | BehaviorEntry source = newConstructor("d", "(II)V"); | 117 | BehaviorEntry source = newConstructor("d", "(II)V"); |
| @@ -115,7 +120,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 115 | containsInAnyOrder("d") | 120 | containsInAnyOrder("d") |
| 116 | ); | 121 | ); |
| 117 | } | 122 | } |
| 118 | 123 | ||
| 119 | @Test | 124 | @Test |
| 120 | public void subsubIntReferences() { | 125 | public void subsubIntReferences() { |
| 121 | BehaviorEntry source = newConstructor("e", "(I)V"); | 126 | BehaviorEntry source = newConstructor("e", "(I)V"); |
| @@ -124,7 +129,7 @@ public class TestTokensConstructors extends TokenChecker { | |||
| 124 | containsInAnyOrder("e") | 129 | containsInAnyOrder("e") |
| 125 | ); | 130 | ); |
| 126 | } | 131 | } |
| 127 | 132 | ||
| 128 | @Test | 133 | @Test |
| 129 | public void defaultConstructableReferences() { | 134 | public void defaultConstructableReferences() { |
| 130 | BehaviorEntry source = newConstructor("c", "()V"); | 135 | BehaviorEntry source = newConstructor("c", "()V"); |
diff --git a/src/test/java/cuchaz/enigma/TestTranslator.java b/src/test/java/cuchaz/enigma/TestTranslator.java index 2c54603b..b63dff86 100644 --- a/src/test/java/cuchaz/enigma/TestTranslator.java +++ b/src/test/java/cuchaz/enigma/TestTranslator.java | |||
| @@ -8,28 +8,29 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import static cuchaz.enigma.TestEntryFactory.*; | ||
| 14 | 11 | ||
| 15 | import org.junit.BeforeClass; | 12 | package cuchaz.enigma; |
| 16 | import org.junit.Test; | ||
| 17 | 13 | ||
| 18 | import cuchaz.enigma.mapping.Entry; | 14 | import cuchaz.enigma.mapping.Entry; |
| 19 | import cuchaz.enigma.mapping.Mappings; | 15 | import cuchaz.enigma.mapping.Mappings; |
| 20 | import cuchaz.enigma.mapping.Translator; | 16 | import cuchaz.enigma.mapping.Translator; |
| 17 | import org.junit.BeforeClass; | ||
| 18 | import org.junit.Test; | ||
| 21 | 19 | ||
| 20 | import static cuchaz.enigma.TestEntryFactory.newClass; | ||
| 21 | import static cuchaz.enigma.TestEntryFactory.newField; | ||
| 22 | import static cuchaz.enigma.TestEntryFactory.newMethod; | ||
| 22 | 23 | ||
| 23 | public class TestTranslator { | 24 | public class TestTranslator { |
| 24 | 25 | ||
| 25 | private static Deobfuscator deobfuscator; | 26 | private static Deobfuscator deobfuscator; |
| 26 | private static Mappings mappings; | 27 | private static Mappings mappings; |
| 27 | private static Translator deobfTranslator; | 28 | private static Translator deobfTranslator; |
| 28 | private static Translator obfTranslator; | 29 | private static Translator obfTranslator; |
| 29 | 30 | ||
| 30 | @BeforeClass | 31 | @BeforeClass |
| 31 | public static void beforeClass() | 32 | public static void beforeClass() |
| 32 | throws Exception { | 33 | throws Exception { |
| 33 | //TODO FIx | 34 | //TODO FIx |
| 34 | //deobfuscator = new Deobfuscator(new JarFile("build/test-obf/translation.jar")); | 35 | //deobfuscator = new Deobfuscator(new JarFile("build/test-obf/translation.jar")); |
| 35 | //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { | 36 | //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { |
diff --git a/src/test/java/cuchaz/enigma/TestType.java b/src/test/java/cuchaz/enigma/TestType.java index 01c235b8..43dacb0c 100644 --- a/src/test/java/cuchaz/enigma/TestType.java +++ b/src/test/java/cuchaz/enigma/TestType.java | |||
| @@ -4,23 +4,23 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import static cuchaz.enigma.TestEntryFactory.*; | ||
| 14 | import static org.hamcrest.MatcherAssert.*; | ||
| 15 | import static org.hamcrest.Matchers.*; | ||
| 16 | 11 | ||
| 17 | import org.junit.Test; | 12 | package cuchaz.enigma; |
| 18 | 13 | ||
| 19 | import cuchaz.enigma.mapping.Type; | 14 | import cuchaz.enigma.mapping.Type; |
| 15 | import org.junit.Test; | ||
| 20 | 16 | ||
| 17 | import static cuchaz.enigma.TestEntryFactory.newClass; | ||
| 18 | import static org.hamcrest.MatcherAssert.assertThat; | ||
| 19 | import static org.hamcrest.Matchers.is; | ||
| 20 | import static org.hamcrest.Matchers.not; | ||
| 21 | 21 | ||
| 22 | public class TestType { | 22 | public class TestType { |
| 23 | 23 | ||
| 24 | @Test | 24 | @Test |
| 25 | public void isVoid() { | 25 | public void isVoid() { |
| 26 | assertThat(new Type("V").isVoid(), is(true)); | 26 | assertThat(new Type("V").isVoid(), is(true)); |
| @@ -34,7 +34,7 @@ public class TestType { | |||
| 34 | assertThat(new Type("LFoo;").isVoid(), is(false)); | 34 | assertThat(new Type("LFoo;").isVoid(), is(false)); |
| 35 | assertThat(new Type("[I").isVoid(), is(false)); | 35 | assertThat(new Type("[I").isVoid(), is(false)); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | @Test | 38 | @Test |
| 39 | public void isPrimitive() { | 39 | public void isPrimitive() { |
| 40 | assertThat(new Type("V").isPrimitive(), is(false)); | 40 | assertThat(new Type("V").isPrimitive(), is(false)); |
| @@ -48,7 +48,7 @@ public class TestType { | |||
| 48 | assertThat(new Type("LFoo;").isPrimitive(), is(false)); | 48 | assertThat(new Type("LFoo;").isPrimitive(), is(false)); |
| 49 | assertThat(new Type("[I").isPrimitive(), is(false)); | 49 | assertThat(new Type("[I").isPrimitive(), is(false)); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | @Test | 52 | @Test |
| 53 | public void getPrimitive() { | 53 | public void getPrimitive() { |
| 54 | assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean)); | 54 | assertThat(new Type("Z").getPrimitive(), is(Type.Primitive.Boolean)); |
| @@ -59,7 +59,7 @@ public class TestType { | |||
| 59 | assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float)); | 59 | assertThat(new Type("F").getPrimitive(), is(Type.Primitive.Float)); |
| 60 | assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double)); | 60 | assertThat(new Type("D").getPrimitive(), is(Type.Primitive.Double)); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | @Test | 63 | @Test |
| 64 | public void isClass() { | 64 | public void isClass() { |
| 65 | assertThat(new Type("V").isClass(), is(false)); | 65 | assertThat(new Type("V").isClass(), is(false)); |
| @@ -73,19 +73,19 @@ public class TestType { | |||
| 73 | assertThat(new Type("LFoo;").isClass(), is(true)); | 73 | assertThat(new Type("LFoo;").isClass(), is(true)); |
| 74 | assertThat(new Type("[I").isClass(), is(false)); | 74 | assertThat(new Type("[I").isClass(), is(false)); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | @Test | 77 | @Test |
| 78 | public void getClassEntry() { | 78 | public void getClassEntry() { |
| 79 | assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); | 79 | assertThat(new Type("LFoo;").getClassEntry(), is(newClass("Foo"))); |
| 80 | assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); | 80 | assertThat(new Type("Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | @Test | 83 | @Test |
| 84 | public void getArrayClassEntry() { | 84 | public void getArrayClassEntry() { |
| 85 | assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo"))); | 85 | assertThat(new Type("[LFoo;").getClassEntry(), is(newClass("Foo"))); |
| 86 | assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); | 86 | assertThat(new Type("[[[Ljava/lang/String;").getClassEntry(), is(newClass("java/lang/String"))); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | @Test | 89 | @Test |
| 90 | public void isArray() { | 90 | public void isArray() { |
| 91 | assertThat(new Type("V").isArray(), is(false)); | 91 | assertThat(new Type("V").isArray(), is(false)); |
| @@ -99,14 +99,14 @@ public class TestType { | |||
| 99 | assertThat(new Type("LFoo;").isArray(), is(false)); | 99 | assertThat(new Type("LFoo;").isArray(), is(false)); |
| 100 | assertThat(new Type("[I").isArray(), is(true)); | 100 | assertThat(new Type("[I").isArray(), is(true)); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | @Test | 103 | @Test |
| 104 | public void getArrayDimension() { | 104 | public void getArrayDimension() { |
| 105 | assertThat(new Type("[I").getArrayDimension(), is(1)); | 105 | assertThat(new Type("[I").getArrayDimension(), is(1)); |
| 106 | assertThat(new Type("[[I").getArrayDimension(), is(2)); | 106 | assertThat(new Type("[[I").getArrayDimension(), is(2)); |
| 107 | assertThat(new Type("[[[I").getArrayDimension(), is(3)); | 107 | assertThat(new Type("[[[I").getArrayDimension(), is(3)); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | @Test | 110 | @Test |
| 111 | public void getArrayType() { | 111 | public void getArrayType() { |
| 112 | assertThat(new Type("[I").getArrayType(), is(new Type("I"))); | 112 | assertThat(new Type("[I").getArrayType(), is(new Type("I"))); |
| @@ -114,7 +114,7 @@ public class TestType { | |||
| 114 | assertThat(new Type("[[[I").getArrayType(), is(new Type("I"))); | 114 | assertThat(new Type("[[[I").getArrayType(), is(new Type("I"))); |
| 115 | assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); | 115 | assertThat(new Type("[Ljava/lang/String;").getArrayType(), is(new Type("Ljava/lang/String;"))); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | @Test | 118 | @Test |
| 119 | public void hasClass() { | 119 | public void hasClass() { |
| 120 | assertThat(new Type("LFoo;").hasClass(), is(true)); | 120 | assertThat(new Type("LFoo;").hasClass(), is(true)); |
| @@ -127,7 +127,7 @@ public class TestType { | |||
| 127 | assertThat(new Type("[[[I").hasClass(), is(false)); | 127 | assertThat(new Type("[[[I").hasClass(), is(false)); |
| 128 | assertThat(new Type("Z").hasClass(), is(false)); | 128 | assertThat(new Type("Z").hasClass(), is(false)); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | @Test | 131 | @Test |
| 132 | public void parseVoid() { | 132 | public void parseVoid() { |
| 133 | final String answer = "V"; | 133 | final String answer = "V"; |
| @@ -138,7 +138,7 @@ public class TestType { | |||
| 138 | assertThat(Type.parseFirst("VLFoo;"), is(answer)); | 138 | assertThat(Type.parseFirst("VLFoo;"), is(answer)); |
| 139 | assertThat(Type.parseFirst("V[LFoo;"), is(answer)); | 139 | assertThat(Type.parseFirst("V[LFoo;"), is(answer)); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | @Test | 142 | @Test |
| 143 | public void parsePrimitive() { | 143 | public void parsePrimitive() { |
| 144 | final String answer = "I"; | 144 | final String answer = "I"; |
| @@ -149,7 +149,7 @@ public class TestType { | |||
| 149 | assertThat(Type.parseFirst("ILFoo;"), is(answer)); | 149 | assertThat(Type.parseFirst("ILFoo;"), is(answer)); |
| 150 | assertThat(Type.parseFirst("I[LFoo;"), is(answer)); | 150 | assertThat(Type.parseFirst("I[LFoo;"), is(answer)); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | @Test | 153 | @Test |
| 154 | public void parseClass() { | 154 | public void parseClass() { |
| 155 | { | 155 | { |
| @@ -199,7 +199,7 @@ public class TestType { | |||
| 199 | assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer)); | 199 | assertThat(Type.parseFirst("[LFoo;LFoo;"), is(answer)); |
| 200 | } | 200 | } |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | @Test | 203 | @Test |
| 204 | public void equals() { | 204 | public void equals() { |
| 205 | assertThat(new Type("V"), is(new Type("V"))); | 205 | assertThat(new Type("V"), is(new Type("V"))); |
| @@ -214,7 +214,7 @@ public class TestType { | |||
| 214 | assertThat(new Type("[I"), is(new Type("[I"))); | 214 | assertThat(new Type("[I"), is(new Type("[I"))); |
| 215 | assertThat(new Type("[[[I"), is(new Type("[[[I"))); | 215 | assertThat(new Type("[[[I"), is(new Type("[[[I"))); |
| 216 | assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); | 216 | assertThat(new Type("[LFoo;"), is(new Type("[LFoo;"))); |
| 217 | 217 | ||
| 218 | assertThat(new Type("V"), is(not(new Type("I")))); | 218 | assertThat(new Type("V"), is(not(new Type("I")))); |
| 219 | assertThat(new Type("I"), is(not(new Type("J")))); | 219 | assertThat(new Type("I"), is(not(new Type("J")))); |
| 220 | assertThat(new Type("I"), is(not(new Type("LBar;")))); | 220 | assertThat(new Type("I"), is(not(new Type("LBar;")))); |
| @@ -224,7 +224,7 @@ public class TestType { | |||
| 224 | assertThat(new Type("[[[I"), is(not(new Type("[I")))); | 224 | assertThat(new Type("[[[I"), is(not(new Type("[I")))); |
| 225 | assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); | 225 | assertThat(new Type("[LFoo;"), is(not(new Type("[LBar;")))); |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | @Test | 228 | @Test |
| 229 | public void testToString() { | 229 | public void testToString() { |
| 230 | assertThat(new Type("V").toString(), is("V")); | 230 | assertThat(new Type("V").toString(), is("V")); |
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java index 07463206..c6ced488 100644 --- a/src/test/java/cuchaz/enigma/TokenChecker.java +++ b/src/test/java/cuchaz/enigma/TokenChecker.java | |||
| @@ -4,34 +4,34 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma; | ||
| 12 | 11 | ||
| 13 | import java.io.IOException; | 12 | package cuchaz.enigma; |
| 14 | import java.util.Collection; | ||
| 15 | import java.util.List; | ||
| 16 | import java.util.jar.JarFile; | ||
| 17 | 13 | ||
| 18 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 19 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 20 | |||
| 21 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 22 | import cuchaz.enigma.analysis.SourceIndex; | 17 | import cuchaz.enigma.analysis.SourceIndex; |
| 23 | import cuchaz.enigma.analysis.Token; | 18 | import cuchaz.enigma.analysis.Token; |
| 24 | import cuchaz.enigma.mapping.Entry; | 19 | import cuchaz.enigma.mapping.Entry; |
| 25 | 20 | ||
| 21 | import java.io.IOException; | ||
| 22 | import java.util.Collection; | ||
| 23 | import java.util.List; | ||
| 24 | import java.util.jar.JarFile; | ||
| 25 | |||
| 26 | public class TokenChecker { | 26 | public class TokenChecker { |
| 27 | 27 | ||
| 28 | private Deobfuscator deobfuscator; | 28 | private Deobfuscator deobfuscator; |
| 29 | 29 | ||
| 30 | protected TokenChecker(JarFile jarFile) | 30 | protected TokenChecker(JarFile jarFile) |
| 31 | throws IOException { | 31 | throws IOException { |
| 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.getClassName()); |
| @@ -39,7 +39,7 @@ public class TokenChecker { | |||
| 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); |
| 41 | SourceIndex index = deobfuscator.getSourceIndex(tree, source); | 41 | SourceIndex index = deobfuscator.getSourceIndex(tree, source); |
| 42 | 42 | ||
| 43 | // get the token value | 43 | // get the token value |
| 44 | Token token = index.getDeclarationToken(entry); | 44 | Token token = index.getDeclarationToken(entry); |
| 45 | if (token == null) { | 45 | if (token == null) { |
| @@ -47,17 +47,17 @@ public class TokenChecker { | |||
| 47 | } | 47 | } |
| 48 | return source.substring(token.start, token.end); | 48 | return source.substring(token.start, token.end); |
| 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.getClassName()); |
| 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; |
diff --git a/src/test/java/cuchaz/enigma/inputs/Keep.java b/src/test/java/cuchaz/enigma/inputs/Keep.java index f04875f5..4dbe8e2f 100644 --- a/src/test/java/cuchaz/enigma/inputs/Keep.java +++ b/src/test/java/cuchaz/enigma/inputs/Keep.java | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs; | 12 | package cuchaz.enigma.inputs; |
| 12 | 13 | ||
| 13 | public class Keep { | 14 | public class Keep { |
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java index ad5e950e..f07e1f8b 100644 --- a/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java | |||
| @@ -4,20 +4,21 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.constructors; | 12 | package cuchaz.enigma.inputs.constructors; |
| 12 | 13 | ||
| 13 | // a | 14 | // a |
| 14 | public class BaseClass { | 15 | public class BaseClass { |
| 15 | 16 | ||
| 16 | // <init>()V | 17 | // <init>()V |
| 17 | public BaseClass() { | 18 | public BaseClass() { |
| 18 | System.out.println("Default constructor"); | 19 | System.out.println("Default constructor"); |
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | // <init>(I)V | 22 | // <init>(I)V |
| 22 | public BaseClass(int i) { | 23 | public BaseClass(int i) { |
| 23 | System.out.println("Int constructor " + i); | 24 | System.out.println("Int constructor " + i); |
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java b/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java index dcd96173..71439fd1 100644 --- a/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java +++ b/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java | |||
| @@ -4,51 +4,52 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.constructors; | 12 | package cuchaz.enigma.inputs.constructors; |
| 12 | 13 | ||
| 13 | // b | 14 | // b |
| 14 | public class Caller { | 15 | public class Caller { |
| 15 | 16 | ||
| 16 | // a()V | 17 | // a()V |
| 17 | public void callBaseDefault() { | 18 | public void callBaseDefault() { |
| 18 | // a.<init>()V | 19 | // a.<init>()V |
| 19 | System.out.println(new BaseClass()); | 20 | System.out.println(new BaseClass()); |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | // b()V | 23 | // b()V |
| 23 | public void callBaseInt() { | 24 | public void callBaseInt() { |
| 24 | // a.<init>(I)V | 25 | // a.<init>(I)V |
| 25 | System.out.println(new BaseClass(5)); | 26 | System.out.println(new BaseClass(5)); |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | // c()V | 29 | // c()V |
| 29 | public void callSubDefault() { | 30 | public void callSubDefault() { |
| 30 | // d.<init>()V | 31 | // d.<init>()V |
| 31 | System.out.println(new SubClass()); | 32 | System.out.println(new SubClass()); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | // d()V | 35 | // d()V |
| 35 | public void callSubInt() { | 36 | public void callSubInt() { |
| 36 | // d.<init>(I)V | 37 | // d.<init>(I)V |
| 37 | System.out.println(new SubClass(6)); | 38 | System.out.println(new SubClass(6)); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | // e()V | 41 | // e()V |
| 41 | public void callSubIntInt() { | 42 | public void callSubIntInt() { |
| 42 | // d.<init>(II)V | 43 | // d.<init>(II)V |
| 43 | System.out.println(new SubClass(4, 2)); | 44 | System.out.println(new SubClass(4, 2)); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | // f()V | 47 | // f()V |
| 47 | public void callSubSubInt() { | 48 | public void callSubSubInt() { |
| 48 | // e.<init>(I)V | 49 | // e.<init>(I)V |
| 49 | System.out.println(new SubSubClass(3)); | 50 | System.out.println(new SubSubClass(3)); |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | // g()V | 53 | // g()V |
| 53 | public void callDefaultConstructable() { | 54 | public void callDefaultConstructable() { |
| 54 | // c.<init>()V | 55 | // c.<init>()V |
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index 655f4da3..c3d41705 100644 --- a/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.constructors; | 12 | package cuchaz.enigma.inputs.constructors; |
| 12 | 13 | ||
| 13 | public class DefaultConstructable { | 14 | public class DefaultConstructable { |
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java index b2934a27..bc56b3b2 100644 --- a/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java | |||
| @@ -4,33 +4,34 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.constructors; | 12 | package cuchaz.enigma.inputs.constructors; |
| 12 | 13 | ||
| 13 | // d extends a | 14 | // d extends a |
| 14 | public class SubClass extends BaseClass { | 15 | public class SubClass extends BaseClass { |
| 15 | 16 | ||
| 16 | // <init>()V | 17 | // <init>()V |
| 17 | public SubClass() { | 18 | public SubClass() { |
| 18 | // a.<init>()V | 19 | // a.<init>()V |
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | // <init>(I)V | 22 | // <init>(I)V |
| 22 | public SubClass(int num) { | 23 | public SubClass(int num) { |
| 23 | // <init>()V | 24 | // <init>()V |
| 24 | this(); | 25 | this(); |
| 25 | System.out.println("SubClass " + num); | 26 | System.out.println("SubClass " + num); |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | // <init>(II)V | 29 | // <init>(II)V |
| 29 | public SubClass(int a, int b) { | 30 | public SubClass(int a, int b) { |
| 30 | // <init>(I)V | 31 | // <init>(I)V |
| 31 | this(a + b); | 32 | this(a + b); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | // <init>(III)V | 35 | // <init>(III)V |
| 35 | public SubClass(int a, int b, int c) { | 36 | public SubClass(int a, int b, int c) { |
| 36 | // a.<init>()V | 37 | // a.<init>()V |
diff --git a/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java b/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java index c1725fea..87b69d32 100644 --- a/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java | |||
| @@ -4,15 +4,16 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.constructors; | 12 | package cuchaz.enigma.inputs.constructors; |
| 12 | 13 | ||
| 13 | // e extends d | 14 | // e extends d |
| 14 | public class SubSubClass extends SubClass { | 15 | public class SubSubClass extends SubClass { |
| 15 | 16 | ||
| 16 | // <init>(I)V | 17 | // <init>(I)V |
| 17 | public SubSubClass(int i) { | 18 | public SubSubClass(int i) { |
| 18 | // c.<init>(I)V | 19 | // c.<init>(I)V |
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index 1b1f3694..b9c4929c 100644 --- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java | |||
| @@ -4,28 +4,29 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.inheritanceTree; | 12 | package cuchaz.enigma.inputs.inheritanceTree; |
| 12 | 13 | ||
| 13 | // a | 14 | // a |
| 14 | public abstract class BaseClass { | 15 | public abstract class BaseClass { |
| 15 | 16 | ||
| 16 | // a | 17 | // a |
| 17 | private String name; | 18 | private String name; |
| 18 | 19 | ||
| 19 | // <init>(Ljava/lang/String;)V | 20 | // <init>(Ljava/lang/String;)V |
| 20 | protected BaseClass(String name) { | 21 | protected BaseClass(String name) { |
| 21 | this.name = name; | 22 | this.name = name; |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | // a()Ljava/lang/String; | 25 | // a()Ljava/lang/String; |
| 25 | public String getName() { | 26 | public String getName() { |
| 26 | return name; | 27 | return name; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | // a()V | 30 | // a()V |
| 30 | public abstract void doBaseThings(); | 31 | public abstract void doBaseThings(); |
| 31 | } | 32 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index d0213a37..50e963c0 100644 --- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java | |||
| @@ -4,15 +4,16 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.inheritanceTree; | 12 | package cuchaz.enigma.inputs.inheritanceTree; |
| 12 | 13 | ||
| 13 | // b extends a | 14 | // b extends a |
| 14 | public abstract class SubclassA extends BaseClass { | 15 | public abstract class SubclassA extends BaseClass { |
| 15 | 16 | ||
| 16 | // <init>(Ljava/lang/String;)V | 17 | // <init>(Ljava/lang/String;)V |
| 17 | protected SubclassA(String name) { | 18 | protected SubclassA(String name) { |
| 18 | // call to a.<init>(Ljava/lang/String)V | 19 | // call to a.<init>(Ljava/lang/String)V |
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index 6d3b0d0f..d0dd664d 100644 --- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java | |||
| @@ -4,34 +4,35 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.inheritanceTree; | 12 | package cuchaz.enigma.inputs.inheritanceTree; |
| 12 | 13 | ||
| 13 | // c extends a | 14 | // c extends a |
| 14 | public class SubclassB extends BaseClass { | 15 | public class SubclassB extends BaseClass { |
| 15 | 16 | ||
| 16 | // a | 17 | // a |
| 17 | private int numThings; | 18 | private int numThings; |
| 18 | 19 | ||
| 19 | // <init>()V | 20 | // <init>()V |
| 20 | protected SubclassB() { | 21 | protected SubclassB() { |
| 21 | // a.<init>(Ljava/lang/String;)V | 22 | // a.<init>(Ljava/lang/String;)V |
| 22 | super("B"); | 23 | super("B"); |
| 23 | 24 | ||
| 24 | // access to a | 25 | // access to a |
| 25 | numThings = 4; | 26 | numThings = 4; |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | @Override | 29 | @Override |
| 29 | // a()V | 30 | // a()V |
| 30 | public void doBaseThings() { | 31 | public void doBaseThings() { |
| 31 | // call to a.a()Ljava/lang/String; | 32 | // call to a.a()Ljava/lang/String; |
| 32 | System.out.println("Base things by B! " + getName()); | 33 | System.out.println("Base things by B! " + getName()); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | // b()V | 36 | // b()V |
| 36 | public void doBThings() { | 37 | public void doBThings() { |
| 37 | // access to a | 38 | // access to a |
diff --git a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index a5b25fd5..c5845702 100644 --- a/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java | |||
| @@ -4,27 +4,28 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.inheritanceTree; | 12 | package cuchaz.enigma.inputs.inheritanceTree; |
| 12 | 13 | ||
| 13 | // d extends b | 14 | // d extends b |
| 14 | public class SubsubclassAA extends SubclassA { | 15 | public class SubsubclassAA extends SubclassA { |
| 15 | 16 | ||
| 16 | protected SubsubclassAA() { | 17 | protected SubsubclassAA() { |
| 17 | // call to b.<init>(Ljava/lang/String;)V | 18 | // call to b.<init>(Ljava/lang/String;)V |
| 18 | super("AA"); | 19 | super("AA"); |
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | @Override | 22 | @Override |
| 22 | // a()Ljava/lang/String; | 23 | // a()Ljava/lang/String; |
| 23 | public String getName() { | 24 | public String getName() { |
| 24 | // call to b.a()Ljava/lang/String; | 25 | // call to b.a()Ljava/lang/String; |
| 25 | return "subsub" + super.getName(); | 26 | return "subsub" + super.getName(); |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | @Override | 29 | @Override |
| 29 | // a()V | 30 | // a()V |
| 30 | public void doBaseThings() { | 31 | public void doBaseThings() { |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java index f6444396..f652d875 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java | |||
| @@ -4,14 +4,15 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.innerClasses; | 12 | package cuchaz.enigma.inputs.innerClasses; |
| 12 | 13 | ||
| 13 | public class A_Anonymous { | 14 | public class A_Anonymous { |
| 14 | 15 | ||
| 15 | public void foo() { | 16 | public void foo() { |
| 16 | Runnable runnable = new Runnable() { | 17 | Runnable runnable = new Runnable() { |
| 17 | @Override | 18 | @Override |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java index d78be847..d1b7601f 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java | |||
| @@ -4,14 +4,15 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.innerClasses; | 12 | package cuchaz.enigma.inputs.innerClasses; |
| 12 | 13 | ||
| 13 | public class B_AnonymousWithScopeArgs { | 14 | public class B_AnonymousWithScopeArgs { |
| 14 | 15 | ||
| 15 | public static void foo(final D_Simple arg) { | 16 | public static void foo(final D_Simple arg) { |
| 16 | System.out.println(new Object() { | 17 | System.out.println(new Object() { |
| 17 | @Override | 18 | @Override |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java index eb03489d..94061faa 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java | |||
| @@ -4,27 +4,28 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.innerClasses; | 12 | package cuchaz.enigma.inputs.innerClasses; |
| 12 | 13 | ||
| 13 | @SuppressWarnings("unused") | 14 | @SuppressWarnings("unused") |
| 14 | public class C_ConstructorArgs { | 15 | public class C_ConstructorArgs { |
| 15 | 16 | ||
| 17 | Inner i; | ||
| 18 | |||
| 19 | public void foo() { | ||
| 20 | i = new Inner(5); | ||
| 21 | } | ||
| 22 | |||
| 16 | class Inner { | 23 | class Inner { |
| 17 | 24 | ||
| 18 | private int a; | 25 | private int a; |
| 19 | 26 | ||
| 20 | public Inner(int a) { | 27 | public Inner(int a) { |
| 21 | this.a = a; | 28 | this.a = a; |
| 22 | } | 29 | } |
| 23 | } | 30 | } |
| 24 | |||
| 25 | Inner i; | ||
| 26 | |||
| 27 | public void foo() { | ||
| 28 | i = new Inner(5); | ||
| 29 | } | ||
| 30 | } | 31 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java index 0e9bf827..71b3a6d8 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java | |||
| @@ -4,14 +4,15 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.innerClasses; | 12 | package cuchaz.enigma.inputs.innerClasses; |
| 12 | 13 | ||
| 13 | public class D_Simple { | 14 | public class D_Simple { |
| 14 | 15 | ||
| 15 | class Inner { | 16 | class Inner { |
| 16 | // nothing to do | 17 | // nothing to do |
| 17 | } | 18 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java index 255434d1..976ec426 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java | |||
| @@ -4,17 +4,18 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.innerClasses; | 12 | package cuchaz.enigma.inputs.innerClasses; |
| 12 | 13 | ||
| 13 | public class E_AnonymousWithOuterAccess { | 14 | public class E_AnonymousWithOuterAccess { |
| 14 | 15 | ||
| 15 | // reproduction of error case documented at: | 16 | // reproduction of error case documented at: |
| 16 | // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating | 17 | // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating |
| 17 | 18 | ||
| 18 | public Object makeInner() { | 19 | public Object makeInner() { |
| 19 | outerMethod(); | 20 | outerMethod(); |
| 20 | return new Object() { | 21 | return new Object() { |
| @@ -24,7 +25,7 @@ public class E_AnonymousWithOuterAccess { | |||
| 24 | } | 25 | } |
| 25 | }; | 26 | }; |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | private String outerMethod() { | 29 | private String outerMethod() { |
| 29 | return "foo"; | 30 | return "foo"; |
| 30 | } | 31 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java b/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java index 7d1dab41..b1de3c9a 100644 --- a/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java +++ b/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java | |||
| @@ -4,25 +4,25 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.inputs.innerClasses; | ||
| 12 | 11 | ||
| 12 | package cuchaz.enigma.inputs.innerClasses; | ||
| 13 | 13 | ||
| 14 | public class F_ClassTree { | 14 | public class F_ClassTree { |
| 15 | 15 | ||
| 16 | public class Level1 { | 16 | public class Level1 { |
| 17 | 17 | ||
| 18 | public int f1; | 18 | public int f1; |
| 19 | 19 | ||
| 20 | public class Level2 { | 20 | public class Level2 { |
| 21 | 21 | ||
| 22 | public int f2; | 22 | public int f2; |
| 23 | 23 | ||
| 24 | public class Level3 { | 24 | public class Level3 { |
| 25 | 25 | ||
| 26 | public int f3; | 26 | public int f3; |
| 27 | } | 27 | } |
| 28 | } | 28 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java b/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java index d28ae97c..ddc4e319 100644 --- a/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java | |||
| @@ -4,20 +4,21 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.loneClass; | 12 | package cuchaz.enigma.inputs.loneClass; |
| 12 | 13 | ||
| 13 | public class LoneClass { | 14 | public class LoneClass { |
| 14 | 15 | ||
| 15 | private String name; | 16 | private String name; |
| 16 | 17 | ||
| 17 | public LoneClass(String name) { | 18 | public LoneClass(String name) { |
| 18 | this.name = name; | 19 | this.name = name; |
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | public String getName() { | 22 | public String getName() { |
| 22 | return name; | 23 | return name; |
| 23 | } | 24 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java b/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java index 26acac8a..26f3718c 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java | |||
| @@ -4,28 +4,29 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | public class A_Basic { | 14 | public class A_Basic { |
| 14 | 15 | ||
| 15 | public int one; | 16 | public int one; |
| 16 | public float two; | 17 | public float two; |
| 17 | public String three; | 18 | public String three; |
| 18 | 19 | ||
| 19 | public void m1() { | 20 | public void m1() { |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | public int m2() { | 23 | public int m2() { |
| 23 | return 42; | 24 | return 42; |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | public void m3(int a1) { | 27 | public void m3(int a1) { |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | public int m4(int a1) { | 30 | public int m4(int a1) { |
| 30 | return 5; // chosen by fair die roll, guaranteed to be random | 31 | return 5; // chosen by fair die roll, guaranteed to be random |
| 31 | } | 32 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java b/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java index 035e3299..fd7f6e7e 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java | |||
| @@ -4,21 +4,22 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | public class B_BaseClass { | 14 | public class B_BaseClass { |
| 14 | 15 | ||
| 15 | public int f1; | 16 | public int f1; |
| 16 | public char f2; | 17 | public char f2; |
| 17 | 18 | ||
| 18 | public int m1() { | 19 | public int m1() { |
| 19 | return 5; | 20 | return 5; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | public int m2() { | 23 | public int m2() { |
| 23 | return 42; | 24 | return 42; |
| 24 | } | 25 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java b/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java index 6026a8d5..9d74e443 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java | |||
| @@ -4,23 +4,24 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | public class C_SubClass extends B_BaseClass { | 14 | public class C_SubClass extends B_BaseClass { |
| 14 | 15 | ||
| 15 | public char f2; // shadows B_BaseClass.f2 | 16 | public char f2; // shadows B_BaseClass.f2 |
| 16 | public int f3; | 17 | public int f3; |
| 17 | public int f4; | 18 | public int f4; |
| 18 | 19 | ||
| 19 | @Override | 20 | @Override |
| 20 | public int m1() { | 21 | public int m1() { |
| 21 | return 32; | 22 | return 32; |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | public int m3() { | 25 | public int m3() { |
| 25 | return 7; | 26 | return 7; |
| 26 | } | 27 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java index a1827f98..99c83bbf 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java | |||
| @@ -4,17 +4,18 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | import java.util.ArrayList; | 14 | import java.util.ArrayList; |
| 14 | import java.util.List; | 15 | import java.util.List; |
| 15 | 16 | ||
| 16 | public class D_AnonymousTesting { | 17 | public class D_AnonymousTesting { |
| 17 | 18 | ||
| 18 | public List<Object> getObjs() { | 19 | public List<Object> getObjs() { |
| 19 | List<Object> objs = new ArrayList<Object>(); | 20 | List<Object> objs = new ArrayList<Object>(); |
| 20 | objs.add(new Object() { | 21 | objs.add(new Object() { |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java b/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java index 769eb70e..0b8cf2a5 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java | |||
| @@ -4,15 +4,15 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | import java.util.Iterator; | 14 | import java.util.Iterator; |
| 14 | 15 | ||
| 15 | |||
| 16 | public class E_Bridges implements Iterator<Object> { | 16 | public class E_Bridges implements Iterator<Object> { |
| 17 | 17 | ||
| 18 | @Override | 18 | @Override |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java index 845d62b0..8a92792a 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java | |||
| @@ -4,17 +4,18 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | @SuppressWarnings("FinalizeCalledExplicitly") | 14 | @SuppressWarnings("FinalizeCalledExplicitly") |
| 14 | public class F_ObjectMethods { | 15 | public class F_ObjectMethods { |
| 15 | 16 | ||
| 16 | public void callEmAll() | 17 | public void callEmAll() |
| 17 | throws Throwable { | 18 | throws Throwable { |
| 18 | clone(); | 19 | clone(); |
| 19 | equals(this); | 20 | equals(this); |
| 20 | finalize(); | 21 | finalize(); |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java b/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java index a2e0dafb..a1e6a85c 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java | |||
| @@ -4,30 +4,30 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.inputs.translation; | ||
| 12 | 11 | ||
| 12 | package cuchaz.enigma.inputs.translation; | ||
| 13 | 13 | ||
| 14 | public class G_OuterClass { | 14 | public class G_OuterClass { |
| 15 | 15 | ||
| 16 | public class A_InnerClass { | 16 | public class A_InnerClass { |
| 17 | 17 | ||
| 18 | public int f1; | 18 | public int f1; |
| 19 | public String f2; | 19 | public String f2; |
| 20 | 20 | ||
| 21 | public void m1() {} | 21 | public void m1() {} |
| 22 | 22 | ||
| 23 | public class A_InnerInnerClass { | 23 | public class A_InnerInnerClass { |
| 24 | 24 | ||
| 25 | public int f3; | 25 | public int f3; |
| 26 | 26 | ||
| 27 | public void m2() {} | 27 | public void m2() {} |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public class B_NamelessClass { | 31 | public class B_NamelessClass { |
| 32 | public class A_NamedInnerClass { | 32 | public class A_NamedInnerClass { |
| 33 | public int f4; | 33 | public int f4; |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java index 1b718a54..013c55ae 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java | |||
| @@ -4,34 +4,36 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.inputs.translation; | ||
| 12 | 11 | ||
| 12 | package cuchaz.enigma.inputs.translation; | ||
| 13 | 13 | ||
| 14 | public class H_NamelessClass { | 14 | public class H_NamelessClass { |
| 15 | 15 | ||
| 16 | public class A_InnerClass { | 16 | public class A_InnerClass { |
| 17 | 17 | ||
| 18 | public int f1; | 18 | public int f1; |
| 19 | public String f2; | 19 | public String f2; |
| 20 | 20 | ||
| 21 | public void m1() {} | 21 | public void m1() {} |
| 22 | 22 | ||
| 23 | public class A_InnerInnerClass { | 23 | public class A_InnerInnerClass { |
| 24 | 24 | ||
| 25 | public int f3; | 25 | public int f3; |
| 26 | 26 | ||
| 27 | public void m2() {} | 27 | public void m2() {} |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public class B_NamelessClass { | 31 | public class B_NamelessClass { |
| 32 | public class A_NamedInnerClass { | 32 | public class A_NamedInnerClass { |
| 33 | public int f4; | 33 | public int f4; |
| 34 | |||
| 34 | public class A_AnotherInnerClass {} | 35 | public class A_AnotherInnerClass {} |
| 36 | |||
| 35 | public class B_YetAnotherInnerClass {} | 37 | public class B_YetAnotherInnerClass {} |
| 36 | } | 38 | } |
| 37 | } | 39 | } |
diff --git a/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java b/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java index 3490f9d9..fd2ebdd5 100644 --- a/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java +++ b/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java | |||
| @@ -4,32 +4,32 @@ | |||
| 4 | * are made available under the terms of the GNU Lesser General Public | 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 | 5 | * License v3.0 which accompanies this distribution, and is available at |
| 6 | * http://www.gnu.org/licenses/lgpl.html | 6 | * http://www.gnu.org/licenses/lgpl.html |
| 7 | * | 7 | * |
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.inputs.translation; | 12 | package cuchaz.enigma.inputs.translation; |
| 12 | 13 | ||
| 13 | import java.util.List; | 14 | import java.util.List; |
| 14 | import java.util.Map; | 15 | import java.util.Map; |
| 15 | 16 | ||
| 16 | |||
| 17 | public class I_Generics { | 17 | public class I_Generics { |
| 18 | 18 | ||
| 19 | public class A_Type { | ||
| 20 | } | ||
| 21 | |||
| 22 | public List<Integer> f1; | 19 | public List<Integer> f1; |
| 23 | public List<A_Type> f2; | 20 | public List<A_Type> f2; |
| 24 | public Map<A_Type,A_Type> f3; | 21 | public Map<A_Type, A_Type> f3; |
| 25 | 22 | public B_Generic<Integer> f5; | |
| 23 | public B_Generic<A_Type> f6; | ||
| 24 | |||
| 25 | public class A_Type { | ||
| 26 | } | ||
| 27 | |||
| 26 | public class B_Generic<T> { | 28 | public class B_Generic<T> { |
| 27 | public T f4; | 29 | public T f4; |
| 30 | |||
| 28 | public T m1() { | 31 | public T m1() { |
| 29 | return null; | 32 | return null; |
| 30 | } | 33 | } |
| 31 | } | 34 | } |
| 32 | |||
| 33 | public B_Generic<Integer> f5; | ||
| 34 | public B_Generic<A_Type> f6; | ||
| 35 | } | 35 | } |