From 94198aa8a5b37092cdfde47488c087bc713a228d Mon Sep 17 00:00:00 2001 From: Dmitry Cherniachenko Date: Tue, 18 Apr 2017 23:34:08 +0200 Subject: [PATCH] Detect external vararg methods too (if available on the classpath) --- .../modules/decompiler/ClasspathHelper.java | 70 +++++++++++++++++++ .../decompiler/exps/InvocationExprent.java | 6 +- testData/results/TestClassLambda.dec | 2 +- testData/results/TestVarArgCalls.dec | 9 ++- 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java new file mode 100644 index 0000000..b8ceb06 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java @@ -0,0 +1,70 @@ +package org.jetbrains.java.decompiler.modules.decompiler; + +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ClasspathHelper { + + private static final Map METHOD_CACHE = Collections.synchronizedMap(new HashMap<>()); + + public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) { + String targetClass = classname.replace('/', '.'); + String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor); + + Method method; + if (METHOD_CACHE.containsKey(methodSignature)) { + method = METHOD_CACHE.get(methodSignature); + } + else { + method = findMethodOnClasspath(targetClass, methodSignature); + METHOD_CACHE.put(methodSignature, method); + } + + return method; + } + + private static Method findMethodOnClasspath(String targetClass, String methodSignature) { + try { + Class cls = Class.forName(targetClass); + for (Method mtd : cls.getMethods()) { + // use contains() to ignore access modifiers and thrown exceptions + if (mtd.toString().contains(methodSignature)) { + return mtd; + } + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private static String buildMethodSignature(String name, MethodDescriptor md) { + StringBuilder sb = new StringBuilder(); + + appendType(sb, md.ret); + sb.append(' ').append(name).append('('); + for (VarType param : md.params) { + appendType(sb, param); + sb.append(','); + } + if (sb.charAt(sb.length() - 1) == ',') { + sb.setLength(sb.length() - 1); + } + sb.append(')'); + + return sb.toString(); + } + + private static void appendType(StringBuilder sb, VarType type) { + sb.append(type.value.replace('/', '.')); + for (int i = 0; i < type.arrayDim; i++) { + sb.append("[]"); + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 7f6f382..5f3c197 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -22,6 +22,7 @@ import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.ClasspathHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; @@ -39,6 +40,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; import org.jetbrains.java.decompiler.util.TextUtil; +import java.lang.reflect.Method; import java.util.*; import java.util.Map.Entry; @@ -392,7 +394,9 @@ public class InvocationExprent extends Exprent { } } else { - // TODO: try to check the class on the classpath + // try to check the class on the classpath + Method mtd = ClasspathHelper.findMethod(classname, name, descriptor); + return mtd != null && mtd.isVarArgs(); } return false; } diff --git a/testData/results/TestClassLambda.dec b/testData/results/TestClassLambda.dec index 751537c..609928c 100644 --- a/testData/results/TestClassLambda.dec +++ b/testData/results/TestClassLambda.dec @@ -15,7 +15,7 @@ public class TestClassLambda { public int field = 0; public void testLambda() { - List var1 = Arrays.asList(new Integer[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4), Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7)});// 29 + List var1 = Arrays.asList(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4), Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7));// 29 int var2 = (int)Math.random();// 30 var1.forEach((var2x) -> {// 32 int var3 = 2 * var2x.intValue();// 33 diff --git a/testData/results/TestVarArgCalls.dec b/testData/results/TestVarArgCalls.dec index 736585f..1ccd16a 100644 --- a/testData/results/TestVarArgCalls.dec +++ b/testData/results/TestVarArgCalls.dec @@ -8,11 +8,11 @@ public class TestVarArgCalls { this.printComplex("Test");// 9 this.printComplex("Test: %[0]s", new String[]{"abc"});// 10 this.printComplex("Test: %[0]s - %[0]s", new String[]{"abc"}, new String[]{"DEF"});// 11 - String.format("Test", new Object[0]);// 13 - String.format("Test: %d", new Object[]{Integer.valueOf(123)});// 14 - String.format("Test: %d - %s", new Object[]{Integer.valueOf(123), "DEF"});// 15 + String.format("Test");// 13 + String.format("Test: %d", Integer.valueOf(123));// 14 + String.format("Test: %d - %s", Integer.valueOf(123), "DEF");// 15 Object[] data = new Object[]{"Hello"};// 17 - String.format("Test: %s", new Object[]{data});// 18 + String.format("Test: %s", (Object)data);// 18 String.format("Test: %s", (Object[])data);// 19 }// 20 @@ -46,7 +46,6 @@ class 'pkg/TestVarArgCalls' { 68 9 6c 9 6f 10 - 71 10 75 10 79 11 81 11