summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Adrian Siekierka2018-10-26 19:42:24 +0200
committerGravatar Adrian Siekierka2018-10-26 19:42:42 +0200
commit97056986946cafdf79005721161edea2087c3b67 (patch)
tree020fede2de6dcb120ebc74569b856bb9f7e2e59d
parentmake rebuild method names discard child-only names. imperfect, but eh (diff)
parent"make sure this is actually a field" properly (diff)
downloadenigma-97056986946cafdf79005721161edea2087c3b67.tar.gz
enigma-97056986946cafdf79005721161edea2087c3b67.tar.xz
enigma-97056986946cafdf79005721161edea2087c3b67.zip
update ASM/Guava, merge ASM port fixes
-rw-r--r--build.gradle8
-rw-r--r--src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java12
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java69
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java28
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java12
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java2
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/AccessFlags.java2
-rw-r--r--src/main/java/cuchaz/enigma/config/Config.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java2
-rw-r--r--src/main/java/oml/ExtraClasspathTypeLoader.java59
-rw-r--r--src/main/java/oml/ast/transformers/InvalidIdentifierFix.java29
-rw-r--r--src/main/java/oml/ast/transformers/Java8Generics.java107
-rw-r--r--src/main/java/oml/ast/transformers/VarargsFixer.java197
13 files changed, 510 insertions, 21 deletions
diff --git a/build.gradle b/build.gradle
index 5a0de102..bcafc1c8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -64,10 +64,14 @@ configurations {
64} 64}
65 65
66dependencies { 66dependencies {
67 compile 'com.google.guava:guava:21.+' 67 compile 'com.google.guava:guava:23.+'
68 compile 'org.bitbucket.mstrobel:procyon-compilertools:0.5.33.8-enigma' 68 compile 'org.bitbucket.mstrobel:procyon-compilertools:0.5.33.8-enigma'
69 compile 'com.google.code.gson:gson:2.8.1' 69 compile 'com.google.code.gson:gson:2.8.1'
70 compile "org.ow2.asm:asm-debug-all:5.1" 70 compile 'org.ow2.asm:asm:6.2.1'
71 compile 'org.ow2.asm:asm-analysis:6.2.1'
72 compile 'org.ow2.asm:asm-commons:6.2.1'
73 compile 'org.ow2.asm:asm-tree:6.2.1'
74 compile 'org.ow2.asm:asm-util:6.2.1'
71 75
72 application 'de.sciss:syntaxpane:1.1.+' 76 application 'de.sciss:syntaxpane:1.1.+'
73 77
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
index 58682e23..fe13321c 100644
--- a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java
@@ -8,6 +8,12 @@ import com.strobel.assembler.metadata.ITypeLoader;
8 * Caching version of {@link ClasspathTypeLoader} 8 * Caching version of {@link ClasspathTypeLoader}
9 */ 9 */
10public class CachingClasspathTypeLoader extends CachingTypeLoader { 10public class CachingClasspathTypeLoader extends CachingTypeLoader {
11 private static ITypeLoader extraClassPathLoader = null;
12
13 public static void setExtraClassPathLoader(ITypeLoader loader){
14 extraClassPathLoader = loader;
15 }
16
11 private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); 17 private final ITypeLoader classpathLoader = new ClasspathTypeLoader();
12 18
13 protected byte[] doLoad(String className) { 19 protected byte[] doLoad(String className) {
@@ -15,6 +21,12 @@ public class CachingClasspathTypeLoader extends CachingTypeLoader {
15 if (classpathLoader.tryLoadType(className, parentBuf)) { 21 if (classpathLoader.tryLoadType(className, parentBuf)) {
16 return parentBuf.array(); 22 return parentBuf.array();
17 } 23 }
24 if (extraClassPathLoader != null){
25 parentBuf.reset();
26 if (extraClassPathLoader.tryLoadType(className, parentBuf)){
27 return parentBuf.array();
28 }
29 }
18 return EMPTY_ARRAY;//need to return *something* as null means no store 30 return EMPTY_ARRAY;//need to return *something* as null means no store
19 } 31 }
20} 32}
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 5980f1fc..0c671589 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -15,6 +15,7 @@ import com.google.common.base.Stopwatch;
15import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps; 16import com.google.common.collect.Maps;
17import com.google.common.collect.Sets; 17import com.google.common.collect.Sets;
18import com.strobel.assembler.metadata.ITypeLoader;
18import com.strobel.assembler.metadata.MetadataSystem; 19import com.strobel.assembler.metadata.MetadataSystem;
19import com.strobel.assembler.metadata.TypeDefinition; 20import com.strobel.assembler.metadata.TypeDefinition;
20import com.strobel.assembler.metadata.TypeReference; 21import com.strobel.assembler.metadata.TypeReference;
@@ -33,14 +34,18 @@ import cuchaz.enigma.mapping.*;
33import cuchaz.enigma.mapping.entry.*; 34import cuchaz.enigma.mapping.entry.*;
34import cuchaz.enigma.throwables.IllegalNameException; 35import cuchaz.enigma.throwables.IllegalNameException;
35import cuchaz.enigma.utils.Utils; 36import cuchaz.enigma.utils.Utils;
37import oml.ast.transformers.InvalidIdentifierFix;
38import oml.ast.transformers.Java8Generics;
36import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; 39import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform;
37import oml.ast.transformers.RemoveObjectCasts; 40import oml.ast.transformers.RemoveObjectCasts;
41import oml.ast.transformers.VarargsFixer;
38import org.objectweb.asm.ClassWriter; 42import org.objectweb.asm.ClassWriter;
39import org.objectweb.asm.Opcodes; 43import org.objectweb.asm.Opcodes;
40import org.objectweb.asm.tree.ClassNode; 44import org.objectweb.asm.tree.ClassNode;
41 45
42import java.io.*; 46import java.io.*;
43import java.util.*; 47import java.util.*;
48import java.util.concurrent.ConcurrentHashMap;
44import java.util.concurrent.atomic.AtomicInteger; 49import java.util.concurrent.atomic.AtomicInteger;
45import java.util.jar.JarEntry; 50import java.util.jar.JarEntry;
46import java.util.jar.JarFile; 51import java.util.jar.JarFile;
@@ -170,6 +175,10 @@ public class Deobfuscator {
170 } 175 }
171 176
172 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { 177 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) {
178 return getSourceTree(className, loader, new NoRetryMetadataSystem(loader));
179 }
180
181 public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader, MetadataSystem metadataSystem) {
173 182
174 // we don't know if this class name is obfuscated or deobfuscated 183 // we don't know if this class name is obfuscated or deobfuscated
175 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 184 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
@@ -188,7 +197,7 @@ public class Deobfuscator {
188 this.settings.setTypeLoader(loader); 197 this.settings.setTypeLoader(loader);
189 198
190 // see if procyon can find the desc 199 // see if procyon can find the desc
191 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); 200 TypeReference type = metadataSystem.lookupType(deobfClassName);
192 if (type == null) { 201 if (type == null) {
193 throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s", 202 throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s",
194 className, deobfClassName, loader.getClassNamesToTry(deobfClassName) 203 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
@@ -278,6 +287,9 @@ public class Deobfuscator {
278 //synchronized to make sure the parallelStream doesn't CME with the cache 287 //synchronized to make sure the parallelStream doesn't CME with the cache
279 ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); 288 ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader());
280 289
290 MetadataSystem metadataSystem = new NoRetryMetadataSystem(typeLoader);
291 metadataSystem.setEagerMethodLoadingEnabled(true);//ensures methods are loaded on classload and prevents race conditions
292
281 // DEOBFUSCATE ALL THE THINGS!! @_@ 293 // DEOBFUSCATE ALL THE THINGS!! @_@
282 Stopwatch stopwatch = Stopwatch.createStarted(); 294 Stopwatch stopwatch = Stopwatch.createStarted();
283 AtomicInteger count = new AtomicInteger(); 295 AtomicInteger count = new AtomicInteger();
@@ -289,7 +301,7 @@ public class Deobfuscator {
289 301
290 try { 302 try {
291 // get the source 303 // get the source
292 CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader); 304 CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader, metadataSystem);
293 305
294 // write the file 306 // write the file
295 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); 307 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java");
@@ -355,28 +367,28 @@ public class Deobfuscator {
355 } 367 }
356 368
357 public void rebuildMethodNames(ProgressListener progress) { 369 public void rebuildMethodNames(ProgressListener progress) {
358 int i = 0; 370 final AtomicInteger i = new AtomicInteger();
359 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>(); 371 Map<ClassMapping, Map<Entry, String>> renameClassMap = new HashMap<>();
360 372
361 progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); 373 progress.init(getMappings().classes().size() * 3, "Rebuilding method names");
362 374
363 for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { 375 Lists.newArrayList(getMappings().classes()).parallelStream().forEach(classMapping -> {
364 progress.onProgress(i++, classMapping.getDeobfName()); 376 progress.onProgress(i.getAndIncrement(), classMapping.getDeobfName());
365 rebuildMethodNames(classMapping, renameClassMap); 377 rebuildMethodNames(classMapping, renameClassMap);
366 } 378 });
367 379
368 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) {
369 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName());
370 380
381 renameClassMap.entrySet().parallelStream().forEach(renameClassMapEntry -> {
382 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
371 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 383 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
372 Entry obfEntry = entry.getKey(); 384 Entry obfEntry = entry.getKey();
373 385
374 removeMapping(obfEntry); 386 removeMapping(obfEntry);
375 } 387 }
376 } 388 });
377 389
378 for (Map.Entry<ClassMapping, Map<Entry, String>> renameClassMapEntry : renameClassMap.entrySet()) { 390 renameClassMap.entrySet().parallelStream().forEach(renameClassMapEntry -> {
379 progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); 391 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
380 392
381 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) { 393 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
382 Entry obfEntry = entry.getKey(); 394 Entry obfEntry = entry.getKey();
@@ -390,7 +402,7 @@ public class Deobfuscator {
390 } 402 }
391 } 403 }
392 } 404 }
393 } 405 });
394 } 406 }
395 407
396 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) { 408 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) {
@@ -680,9 +692,12 @@ public class Deobfuscator {
680 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){ 692 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){
681 List<IAstTransform> transformers = Arrays.asList( 693 List<IAstTransform> transformers = Arrays.asList(
682 new ObfuscatedEnumSwitchRewriterTransform(context), 694 new ObfuscatedEnumSwitchRewriterTransform(context),
683 new RemoveObjectCasts(context) 695 new VarargsFixer(context),
696 new RemoveObjectCasts(context),
697 new Java8Generics(),
698 new InvalidIdentifierFix()
684 ); 699 );
685 for (IAstTransform transform : transformers){ 700 for (IAstTransform transform : transformers) {
686 transform.run(builder.getCompilationUnit()); 701 transform.run(builder.getCompilationUnit());
687 } 702 }
688 } 703 }
@@ -697,4 +712,30 @@ public class Deobfuscator {
697 String transform(ClassNode node, ClassWriter writer); 712 String transform(ClassNode node, ClassWriter writer);
698 } 713 }
699 714
715 public static class NoRetryMetadataSystem extends MetadataSystem {
716 private final Set<String> _failedTypes = Collections.newSetFromMap(new ConcurrentHashMap<>());
717
718 public NoRetryMetadataSystem(final ITypeLoader typeLoader) {
719 super(typeLoader);
720 }
721
722 @Override
723 protected synchronized TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive) {
724 if (_failedTypes.contains(descriptor)) {
725 return null;
726 }
727
728 final TypeDefinition result = super.resolveType(descriptor, mightBePrimitive);
729
730 if (result == null) {
731 _failedTypes.add(descriptor);
732 }
733
734 return result;
735 }
736
737 public synchronized TypeDefinition resolve(final TypeReference type) {
738 return super.resolve(type);
739 }
740 }
700} 741}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index eb780ee9..42ceec4d 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -22,7 +22,10 @@ import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
22import cuchaz.enigma.mapping.Translator; 22import cuchaz.enigma.mapping.Translator;
23import org.objectweb.asm.ClassWriter; 23import org.objectweb.asm.ClassWriter;
24import org.objectweb.asm.Opcodes; 24import org.objectweb.asm.Opcodes;
25import org.objectweb.asm.tree.AbstractInsnNode;
25import org.objectweb.asm.tree.ClassNode; 26import org.objectweb.asm.tree.ClassNode;
27import org.objectweb.asm.tree.MethodInsnNode;
28import org.objectweb.asm.tree.MethodNode;
26 29
27import java.util.List; 30import java.util.List;
28 31
@@ -89,6 +92,31 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla
89 return null; 92 return null;
90 } 93 }
91 94
95
96 // remove <obj>.getClass() calls that are seemingly injected
97 // DUP
98 // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
99 // POP
100 for (MethodNode methodNode : node.methods){
101 AbstractInsnNode insnNode = methodNode.instructions.getFirst();
102 while (insnNode != null){
103 if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL){
104 MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode;
105 if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")){
106 AbstractInsnNode previous = methodInsnNode.getPrevious();
107 AbstractInsnNode next = methodInsnNode.getNext();
108 if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP){
109 insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction
110 methodNode.instructions.remove(previous);
111 methodNode.instructions.remove(methodInsnNode);
112 methodNode.instructions.remove(next);
113 }
114 }
115 }
116 insnNode = insnNode.getNext();
117 }
118 }
119
92 ClassWriter writer = new ClassWriter(0); 120 ClassWriter writer = new ClassWriter(0);
93 transformInto(node, writer); 121 transformInto(node, writer);
94 122
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
index 14b2e768..78195cb7 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -17,15 +17,19 @@ import com.google.common.collect.Maps;
17import com.google.common.collect.Multimap; 17import com.google.common.collect.Multimap;
18import com.strobel.decompiler.languages.Region; 18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode; 19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration;
20import com.strobel.decompiler.languages.java.ast.Identifier; 21import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
21import cuchaz.enigma.mapping.entry.Entry; 23import cuchaz.enigma.mapping.entry.Entry;
22 24
23import java.util.Collection; 25import java.util.Collection;
24import java.util.List; 26import java.util.List;
25import java.util.Map; 27import java.util.Map;
26import java.util.TreeMap; 28import java.util.TreeMap;
29import java.util.regex.Pattern;
27 30
28public class SourceIndex { 31public class SourceIndex {
32 private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$");
29 33
30 private String source; 34 private String source;
31 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference; 35 private TreeMap<Token, EntryReference<Entry, Entry>> tokenToReference;
@@ -81,6 +85,14 @@ public class SourceIndex {
81 return null; 85 return null;
82 } 86 }
83 87
88 if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){
89 TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null;
90 if (type != null){
91 name = type.getName();
92 token.end = token.start + name.length();
93 }
94 }
95
84 // DEBUG 96 // DEBUG
85 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); 97 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
86 98
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
index 83fe296c..83e5e04f 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java
@@ -76,7 +76,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor {
76 @Override 76 @Override
77 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { 77 public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) {
78 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 78 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
79 if (ref != null) { 79 if (ref instanceof FieldReference) {
80 // make sure this is actually a field 80 // make sure this is actually a field
81 String erasedSignature = ref.getErasedSignature(); 81 String erasedSignature = ref.getErasedSignature();
82 if (erasedSignature.indexOf('(') >= 0) { 82 if (erasedSignature.indexOf('(') >= 0) {
diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
index 21b24897..0bfc59bd 100644
--- a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
+++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java
@@ -51,7 +51,7 @@ public class AccessFlags {
51 } 51 }
52 52
53 public AccessFlags setBridged() { 53 public AccessFlags setBridged() {
54 this.setVisibility(Opcodes.ACC_BRIDGE); 54 flags |= Opcodes.ACC_BRIDGE;
55 return this; 55 return this;
56 } 56 }
57 57
diff --git a/src/main/java/cuchaz/enigma/config/Config.java b/src/main/java/cuchaz/enigma/config/Config.java
index 87ef3531..75ced70c 100644
--- a/src/main/java/cuchaz/enigma/config/Config.java
+++ b/src/main/java/cuchaz/enigma/config/Config.java
@@ -62,7 +62,7 @@ public class Config {
62 public void loadConfig() throws IOException { 62 public void loadConfig() throws IOException {
63 if (!ENIGMA_DIR.exists()) ENIGMA_DIR.mkdirs(); 63 if (!ENIGMA_DIR.exists()) ENIGMA_DIR.mkdirs();
64 File configFile = new File(ENIGMA_DIR, "config.json"); 64 File configFile = new File(ENIGMA_DIR, "config.json");
65 if (configFile.exists()) gson.fromJson(Files.toString(configFile, Charset.defaultCharset()), Config.class); 65 if (configFile.exists()) gson.fromJson(Files.asCharSource(configFile, Charset.defaultCharset()).read(), Config.class);
66 else { 66 else {
67 this.reset(); 67 this.reset();
68 Files.touch(configFile); 68 Files.touch(configFile);
@@ -71,7 +71,7 @@ public class Config {
71 } 71 }
72 72
73 public void saveConfig() throws IOException { 73 public void saveConfig() throws IOException {
74 Files.write(gson.toJson(this), CONFIG_FILE, Charset.defaultCharset()); 74 Files.asCharSink(CONFIG_FILE, Charset.defaultCharset()).write(gson.toJson(this));
75 } 75 }
76 76
77 public void reset() throws IOException { 77 public void reset() throws IOException {
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
index fd30e389..ffceae12 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
@@ -56,7 +56,7 @@ public class PanelEditor extends JEditorPane {
56 gui.popupMenu.showCallsMenu.doClick(); 56 gui.popupMenu.showCallsMenu.doClick();
57 break; 57 break;
58 58
59 case KeyEvent.VK_T: 59 case KeyEvent.VK_O:
60 gui.popupMenu.toggleMappingMenu.doClick(); 60 gui.popupMenu.toggleMappingMenu.doClick();
61 break; 61 break;
62 case KeyEvent.VK_F5: 62 case KeyEvent.VK_F5:
diff --git a/src/main/java/oml/ExtraClasspathTypeLoader.java b/src/main/java/oml/ExtraClasspathTypeLoader.java
new file mode 100644
index 00000000..f8ec2e0c
--- /dev/null
+++ b/src/main/java/oml/ExtraClasspathTypeLoader.java
@@ -0,0 +1,59 @@
1package oml;
2
3import com.strobel.assembler.metadata.Buffer;
4import com.strobel.assembler.metadata.ITypeLoader;
5
6import java.io.File;
7import java.io.IOException;
8import java.io.InputStream;
9import java.net.MalformedURLException;
10import java.net.URL;
11import java.net.URLClassLoader;
12import java.util.Arrays;
13
14/**
15 * Copy of ClasspathTypeLoader supporting a classpath constructor.
16 */
17public class ExtraClasspathTypeLoader implements ITypeLoader {
18 private final ClassLoader _loader;
19
20 public ExtraClasspathTypeLoader(String extraClasspath){
21 _loader = new URLClassLoader(Arrays.stream(extraClasspath.split(File.pathSeparator)).map(path-> {
22 try {
23 return new File(path).toURI().toURL();
24 } catch (MalformedURLException e) {
25 throw new RuntimeException(e);
26 }
27 }).toArray(URL[]::new));
28 }
29
30 @Override
31 public boolean tryLoadType(final String internalName, final Buffer buffer) {
32
33 final String path = internalName.concat(".class");
34 final URL resource = _loader.getResource(path);
35
36 if (resource == null) {
37 return false;
38 }
39
40 try (final InputStream stream = _loader.getResourceAsStream(path)) {
41 final byte[] temp = new byte[4096];
42
43 int bytesRead;
44
45 while ((bytesRead = stream.read(temp, 0, temp.length)) > 0) {
46 //buffer.ensureWriteableBytes(bytesRead);
47 buffer.putByteArray(temp, 0, bytesRead);
48 }
49
50 buffer.flip();
51
52
53 return true;
54 }
55 catch (final IOException ignored) {
56 return false;
57 }
58 }
59}
diff --git a/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java b/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java
new file mode 100644
index 00000000..3e052ded
--- /dev/null
+++ b/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java
@@ -0,0 +1,29 @@
1package oml.ast.transformers;
2
3import com.strobel.decompiler.languages.java.ast.AstNode;
4import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
5import com.strobel.decompiler.languages.java.ast.Identifier;
6import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
7
8/**
9 * Created by Thiakil on 13/07/2018.
10 */
11public class InvalidIdentifierFix implements IAstTransform {
12 @Override
13 public void run(AstNode compilationUnit) {
14 compilationUnit.acceptVisitor(new Visitor(), null);
15 }
16
17 class Visitor extends DepthFirstAstVisitor<Void,Void>{
18 @Override
19 public Void visitIdentifier(Identifier node, Void data) {
20 super.visitIdentifier(node, data);
21 if (node.getName().equals("do") || node.getName().equals("if")){
22 Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation());
23 newIdentifier.copyUserDataFrom(node);
24 node.replaceWith(newIdentifier);
25 }
26 return null;
27 }
28 }
29}
diff --git a/src/main/java/oml/ast/transformers/Java8Generics.java b/src/main/java/oml/ast/transformers/Java8Generics.java
new file mode 100644
index 00000000..0f8a84c1
--- /dev/null
+++ b/src/main/java/oml/ast/transformers/Java8Generics.java
@@ -0,0 +1,107 @@
1package oml.ast.transformers;
2
3import com.strobel.assembler.metadata.BuiltinTypes;
4import com.strobel.assembler.metadata.CommonTypeReferences;
5import com.strobel.assembler.metadata.Flags;
6import com.strobel.assembler.metadata.IGenericInstance;
7import com.strobel.assembler.metadata.IMemberDefinition;
8import com.strobel.assembler.metadata.JvmType;
9import com.strobel.assembler.metadata.MemberReference;
10import com.strobel.assembler.metadata.MethodDefinition;
11import com.strobel.assembler.metadata.TypeDefinition;
12import com.strobel.assembler.metadata.TypeReference;
13import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
14import com.strobel.decompiler.languages.java.ast.AstNode;
15import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
16import com.strobel.decompiler.languages.java.ast.AstType;
17import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ComposedType;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.Identifier;
22import com.strobel.decompiler.languages.java.ast.InvocationExpression;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.Roles;
27import com.strobel.decompiler.languages.java.ast.SimpleType;
28import com.strobel.decompiler.languages.java.ast.WildcardType;
29import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
30
31/**
32 * Created by Thiakil on 12/07/2018.
33 */
34public class Java8Generics implements IAstTransform {
35
36 @Override
37 public void run(AstNode compilationUnit) {
38 compilationUnit.acceptVisitor(new Visitor(), null);
39 }
40
41 static class Visitor extends DepthFirstAstVisitor<Void,Void>{
42
43 @Override
44 public Void visitInvocationExpression(InvocationExpression node, Void data) {
45 super.visitInvocationExpression(node, data);
46 if (node.getTarget() instanceof MemberReferenceExpression){
47 MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget();
48 if (referenceExpression.getTypeArguments().stream().map(t->{
49 TypeReference tr = t.toTypeReference();
50 if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below
51 TypeReference resolved = tr.resolve();
52 if (resolved != null)
53 return resolved;
54 }
55 return tr;
56 }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) {
57 //these are invalid for invocations, let the compiler work it out
58 referenceExpression.getTypeArguments().clear();
59 } else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){
60 //all are <Object>, thereby redundant and/or bad
61 referenceExpression.getTypeArguments().clear();
62 }
63 }
64 return null;
65 }
66
67 @Override
68 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
69 super.visitObjectCreationExpression(node, data);
70 AstType type = node.getType();
71 if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){
72 SimpleType simpleType = (SimpleType) type;
73 AstNodeCollection<AstType> typeArguments = simpleType.getTypeArguments();
74 if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){
75 //all are <Object>, thereby redundant and/or bad
76 typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create(""));
77 }
78 }
79 return null;
80 }
81
82 @Override
83 public Void visitCastExpression(CastExpression node, Void data) {
84 boolean doReplace = false;
85 TypeReference typeReference = node.getType().toTypeReference();
86 if (typeReference.isArray() && typeReference.getElementType().isGenericType()){
87 doReplace = true;
88 } else if (typeReference.isGenericType()) {
89 Expression target = node.getExpression();
90 if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){
91 doReplace = true;
92 } else if (target instanceof InvocationExpression) {
93 InvocationExpression invocationExpression = (InvocationExpression)target;
94 if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) {
95 ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear();
96 doReplace = true;
97 }
98 }
99 }
100 super.visitCastExpression(node, data);
101 if (doReplace){
102 node.replaceWith(node.getExpression());
103 }
104 return null;
105 }
106 }
107}
diff --git a/src/main/java/oml/ast/transformers/VarargsFixer.java b/src/main/java/oml/ast/transformers/VarargsFixer.java
new file mode 100644
index 00000000..5810373d
--- /dev/null
+++ b/src/main/java/oml/ast/transformers/VarargsFixer.java
@@ -0,0 +1,197 @@
1package oml.ast.transformers;
2
3import com.strobel.assembler.metadata.MemberReference;
4import com.strobel.assembler.metadata.MetadataFilters;
5import com.strobel.assembler.metadata.MetadataHelper;
6import com.strobel.assembler.metadata.MethodBinder;
7import com.strobel.assembler.metadata.MethodDefinition;
8import com.strobel.assembler.metadata.MethodReference;
9import com.strobel.assembler.metadata.TypeReference;
10import com.strobel.core.StringUtilities;
11import com.strobel.core.VerifyArgument;
12import com.strobel.decompiler.DecompilerContext;
13import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression;
14import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression;
15import com.strobel.decompiler.languages.java.ast.AstNode;
16import com.strobel.decompiler.languages.java.ast.AstNodeCollection;
17import com.strobel.decompiler.languages.java.ast.CastExpression;
18import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
19import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor;
20import com.strobel.decompiler.languages.java.ast.Expression;
21import com.strobel.decompiler.languages.java.ast.InvocationExpression;
22import com.strobel.decompiler.languages.java.ast.JavaResolver;
23import com.strobel.decompiler.languages.java.ast.Keys;
24import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
25import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression;
26import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
27import com.strobel.decompiler.semantics.ResolveResult;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Created by Thiakil on 12/07/2018.
34 */
35public class VarargsFixer implements IAstTransform {
36 private final DecompilerContext _context;
37
38 public VarargsFixer(final DecompilerContext context) {
39 _context = VerifyArgument.notNull(context, "context");
40 }
41
42 @Override
43 public void run(AstNode compilationUnit) {
44 compilationUnit.acceptVisitor(new Visitor(_context), null);
45 }
46
47 class Visitor extends ContextTrackingVisitor<Void> {
48 private final JavaResolver _resolver;
49 protected Visitor(DecompilerContext context) {
50 super(context);
51 _resolver = new JavaResolver(context);
52 }
53
54 //remove `new Object[0]` on varagrs as the normal tranformer doesnt do them
55 @Override
56 public Void visitInvocationExpression(InvocationExpression node, Void data) {
57 super.visitInvocationExpression(node, data);
58 MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE);
59 if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()){
60 AstNodeCollection<Expression> arguments = node.getArguments();
61 Expression lastParam = arguments.lastOrNullObject();
62 if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression){
63 ArrayCreationExpression varargArray = (ArrayCreationExpression)lastParam;
64 if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()){
65 lastParam.remove();
66 } else {
67 for (Expression e : varargArray.getInitializer().getElements()){
68 arguments.insertBefore(varargArray, e.clone());
69 }
70 varargArray.remove();
71 }
72 }
73 }
74 return null;
75 }
76
77 //applies the vararg transform to object creation
78 @Override
79 public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) {
80 super.visitObjectCreationExpression(node, data);
81 final AstNodeCollection<Expression> arguments = node.getArguments();
82 final Expression lastArgument = arguments.lastOrNullObject();
83
84 Expression arrayArg = lastArgument;
85
86 if (arrayArg instanceof CastExpression)
87 arrayArg = ((CastExpression) arrayArg).getExpression();
88
89 if (arrayArg == null ||
90 arrayArg.isNull() ||
91 !(arrayArg instanceof ArrayCreationExpression &&
92 node.getTarget() instanceof MemberReferenceExpression)) {
93
94 return null;
95 }
96
97 final ArrayCreationExpression newArray = (ArrayCreationExpression) arrayArg;
98 final MemberReferenceExpression target = (MemberReferenceExpression) node.getTarget();
99
100 if (!newArray.getAdditionalArraySpecifiers().hasSingleElement()) {
101 return null;
102 }
103
104 final MethodReference method = (MethodReference) node.getUserData(Keys.MEMBER_REFERENCE);
105
106 if (method == null) {
107 return null;
108 }
109
110 final MethodDefinition resolved = method.resolve();
111
112 if (resolved == null || !resolved.isVarArgs()) {
113 return null;
114 }
115
116 final List<MethodReference> candidates;
117 final Expression invocationTarget = target.getTarget();
118
119 if (invocationTarget == null || invocationTarget.isNull()) {
120 candidates = MetadataHelper.findMethods(
121 context.getCurrentType(),
122 MetadataFilters.matchName(resolved.getName())
123 );
124 }
125 else {
126 final ResolveResult targetResult = _resolver.apply(invocationTarget);
127
128 if (targetResult == null || targetResult.getType() == null) {
129 return null;
130 }
131
132 candidates = MetadataHelper.findMethods(
133 targetResult.getType(),
134 MetadataFilters.matchName(resolved.getName())
135 );
136 }
137
138 final List<TypeReference> argTypes = new ArrayList<>();
139
140 for (final Expression argument : arguments) {
141 final ResolveResult argResult = _resolver.apply(argument);
142
143 if (argResult == null || argResult.getType() == null) {
144 return null;
145 }
146
147 argTypes.add(argResult.getType());
148 }
149
150 final MethodBinder.BindResult c1 = MethodBinder.selectMethod(candidates, argTypes);
151
152 if (c1.isFailure() || c1.isAmbiguous()) {
153 return null;
154 }
155
156 argTypes.remove(argTypes.size() - 1);
157
158 final ArrayInitializerExpression initializer = newArray.getInitializer();
159 final boolean hasElements = !initializer.isNull() && !initializer.getElements().isEmpty();
160
161 if (hasElements) {
162 for (final Expression argument : initializer.getElements()) {
163 final ResolveResult argResult = _resolver.apply(argument);
164
165 if (argResult == null || argResult.getType() == null) {
166 return null;
167 }
168
169 argTypes.add(argResult.getType());
170 }
171 }
172
173 final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes);
174
175 if (c2.isFailure() ||
176 c2.isAmbiguous() ||
177 !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) {
178
179 return null;
180 }
181
182 lastArgument.remove();
183
184 if (!hasElements) {
185 lastArgument.remove();
186 return null;
187 }
188
189 for (final Expression newArg : initializer.getElements()) {
190 newArg.remove();
191 arguments.add(newArg);
192 }
193
194 return null;
195 }
196 }
197}