diff options
| author | 2018-10-26 19:42:24 +0200 | |
|---|---|---|
| committer | 2018-10-26 19:42:42 +0200 | |
| commit | 97056986946cafdf79005721161edea2087c3b67 (patch) | |
| tree | 020fede2de6dcb120ebc74569b856bb9f7e2e59d | |
| parent | make rebuild method names discard child-only names. imperfect, but eh (diff) | |
| parent | "make sure this is actually a field" properly (diff) | |
| download | enigma-97056986946cafdf79005721161edea2087c3b67.tar.gz enigma-97056986946cafdf79005721161edea2087c3b67.tar.xz enigma-97056986946cafdf79005721161edea2087c3b67.zip | |
update ASM/Guava, merge ASM port fixes
| -rw-r--r-- | build.gradle | 8 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java | 12 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/Deobfuscator.java | 69 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/TranslatingTypeLoader.java | 28 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/analysis/SourceIndex.java | 12 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java | 2 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/bytecode/AccessFlags.java | 2 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/config/Config.java | 4 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java | 2 | ||||
| -rw-r--r-- | src/main/java/oml/ExtraClasspathTypeLoader.java | 59 | ||||
| -rw-r--r-- | src/main/java/oml/ast/transformers/InvalidIdentifierFix.java | 29 | ||||
| -rw-r--r-- | src/main/java/oml/ast/transformers/Java8Generics.java | 107 | ||||
| -rw-r--r-- | src/main/java/oml/ast/transformers/VarargsFixer.java | 197 |
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 | ||
| 66 | dependencies { | 66 | dependencies { |
| 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 | */ |
| 10 | public class CachingClasspathTypeLoader extends CachingTypeLoader { | 10 | public 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; | |||
| 15 | import com.google.common.collect.Lists; | 15 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 17 | import com.google.common.collect.Sets; | 17 | import com.google.common.collect.Sets; |
| 18 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 18 | import com.strobel.assembler.metadata.MetadataSystem; | 19 | import com.strobel.assembler.metadata.MetadataSystem; |
| 19 | import com.strobel.assembler.metadata.TypeDefinition; | 20 | import com.strobel.assembler.metadata.TypeDefinition; |
| 20 | import com.strobel.assembler.metadata.TypeReference; | 21 | import com.strobel.assembler.metadata.TypeReference; |
| @@ -33,14 +34,18 @@ import cuchaz.enigma.mapping.*; | |||
| 33 | import cuchaz.enigma.mapping.entry.*; | 34 | import cuchaz.enigma.mapping.entry.*; |
| 34 | import cuchaz.enigma.throwables.IllegalNameException; | 35 | import cuchaz.enigma.throwables.IllegalNameException; |
| 35 | import cuchaz.enigma.utils.Utils; | 36 | import cuchaz.enigma.utils.Utils; |
| 37 | import oml.ast.transformers.InvalidIdentifierFix; | ||
| 38 | import oml.ast.transformers.Java8Generics; | ||
| 36 | import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; | 39 | import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; |
| 37 | import oml.ast.transformers.RemoveObjectCasts; | 40 | import oml.ast.transformers.RemoveObjectCasts; |
| 41 | import oml.ast.transformers.VarargsFixer; | ||
| 38 | import org.objectweb.asm.ClassWriter; | 42 | import org.objectweb.asm.ClassWriter; |
| 39 | import org.objectweb.asm.Opcodes; | 43 | import org.objectweb.asm.Opcodes; |
| 40 | import org.objectweb.asm.tree.ClassNode; | 44 | import org.objectweb.asm.tree.ClassNode; |
| 41 | 45 | ||
| 42 | import java.io.*; | 46 | import java.io.*; |
| 43 | import java.util.*; | 47 | import java.util.*; |
| 48 | import java.util.concurrent.ConcurrentHashMap; | ||
| 44 | import java.util.concurrent.atomic.AtomicInteger; | 49 | import java.util.concurrent.atomic.AtomicInteger; |
| 45 | import java.util.jar.JarEntry; | 50 | import java.util.jar.JarEntry; |
| 46 | import java.util.jar.JarFile; | 51 | import 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; | |||
| 22 | import cuchaz.enigma.mapping.Translator; | 22 | import cuchaz.enigma.mapping.Translator; |
| 23 | import org.objectweb.asm.ClassWriter; | 23 | import org.objectweb.asm.ClassWriter; |
| 24 | import org.objectweb.asm.Opcodes; | 24 | import org.objectweb.asm.Opcodes; |
| 25 | import org.objectweb.asm.tree.AbstractInsnNode; | ||
| 25 | import org.objectweb.asm.tree.ClassNode; | 26 | import org.objectweb.asm.tree.ClassNode; |
| 27 | import org.objectweb.asm.tree.MethodInsnNode; | ||
| 28 | import org.objectweb.asm.tree.MethodNode; | ||
| 26 | 29 | ||
| 27 | import java.util.List; | 30 | import 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; | |||
| 17 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 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.ConstructorDeclaration; | ||
| 20 | import com.strobel.decompiler.languages.java.ast.Identifier; | 21 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 22 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | ||
| 21 | import cuchaz.enigma.mapping.entry.Entry; | 23 | import cuchaz.enigma.mapping.entry.Entry; |
| 22 | 24 | ||
| 23 | import java.util.Collection; | 25 | import java.util.Collection; |
| 24 | import java.util.List; | 26 | import java.util.List; |
| 25 | import java.util.Map; | 27 | import java.util.Map; |
| 26 | import java.util.TreeMap; | 28 | import java.util.TreeMap; |
| 29 | import java.util.regex.Pattern; | ||
| 27 | 30 | ||
| 28 | public class SourceIndex { | 31 | public 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 @@ | |||
| 1 | package oml; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 5 | |||
| 6 | import java.io.File; | ||
| 7 | import java.io.IOException; | ||
| 8 | import java.io.InputStream; | ||
| 9 | import java.net.MalformedURLException; | ||
| 10 | import java.net.URL; | ||
| 11 | import java.net.URLClassLoader; | ||
| 12 | import java.util.Arrays; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Copy of ClasspathTypeLoader supporting a classpath constructor. | ||
| 16 | */ | ||
| 17 | public 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 @@ | |||
| 1 | package oml.ast.transformers; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 4 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 5 | import com.strobel.decompiler.languages.java.ast.Identifier; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 7 | |||
| 8 | /** | ||
| 9 | * Created by Thiakil on 13/07/2018. | ||
| 10 | */ | ||
| 11 | public 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 @@ | |||
| 1 | package oml.ast.transformers; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.BuiltinTypes; | ||
| 4 | import com.strobel.assembler.metadata.CommonTypeReferences; | ||
| 5 | import com.strobel.assembler.metadata.Flags; | ||
| 6 | import com.strobel.assembler.metadata.IGenericInstance; | ||
| 7 | import com.strobel.assembler.metadata.IMemberDefinition; | ||
| 8 | import com.strobel.assembler.metadata.JvmType; | ||
| 9 | import com.strobel.assembler.metadata.MemberReference; | ||
| 10 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 11 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 12 | import com.strobel.assembler.metadata.TypeReference; | ||
| 13 | import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; | ||
| 14 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.AstNodeCollection; | ||
| 16 | import com.strobel.decompiler.languages.java.ast.AstType; | ||
| 17 | import com.strobel.decompiler.languages.java.ast.CastExpression; | ||
| 18 | import com.strobel.decompiler.languages.java.ast.ComposedType; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 20 | import com.strobel.decompiler.languages.java.ast.Expression; | ||
| 21 | import com.strobel.decompiler.languages.java.ast.Identifier; | ||
| 22 | import com.strobel.decompiler.languages.java.ast.InvocationExpression; | ||
| 23 | import com.strobel.decompiler.languages.java.ast.Keys; | ||
| 24 | import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; | ||
| 25 | import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; | ||
| 26 | import com.strobel.decompiler.languages.java.ast.Roles; | ||
| 27 | import com.strobel.decompiler.languages.java.ast.SimpleType; | ||
| 28 | import com.strobel.decompiler.languages.java.ast.WildcardType; | ||
| 29 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Created by Thiakil on 12/07/2018. | ||
| 33 | */ | ||
| 34 | public 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 @@ | |||
| 1 | package oml.ast.transformers; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.MemberReference; | ||
| 4 | import com.strobel.assembler.metadata.MetadataFilters; | ||
| 5 | import com.strobel.assembler.metadata.MetadataHelper; | ||
| 6 | import com.strobel.assembler.metadata.MethodBinder; | ||
| 7 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 8 | import com.strobel.assembler.metadata.MethodReference; | ||
| 9 | import com.strobel.assembler.metadata.TypeReference; | ||
| 10 | import com.strobel.core.StringUtilities; | ||
| 11 | import com.strobel.core.VerifyArgument; | ||
| 12 | import com.strobel.decompiler.DecompilerContext; | ||
| 13 | import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; | ||
| 14 | import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; | ||
| 15 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 16 | import com.strobel.decompiler.languages.java.ast.AstNodeCollection; | ||
| 17 | import com.strobel.decompiler.languages.java.ast.CastExpression; | ||
| 18 | import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; | ||
| 19 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 20 | import com.strobel.decompiler.languages.java.ast.Expression; | ||
| 21 | import com.strobel.decompiler.languages.java.ast.InvocationExpression; | ||
| 22 | import com.strobel.decompiler.languages.java.ast.JavaResolver; | ||
| 23 | import com.strobel.decompiler.languages.java.ast.Keys; | ||
| 24 | import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; | ||
| 25 | import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; | ||
| 26 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 27 | import com.strobel.decompiler.semantics.ResolveResult; | ||
| 28 | |||
| 29 | import java.util.ArrayList; | ||
| 30 | import java.util.List; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Created by Thiakil on 12/07/2018. | ||
| 34 | */ | ||
| 35 | public 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 | } | ||