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 f56a805..7f6f382 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -343,14 +343,18 @@ public class InvocationExprent extends Exprent { BitSet setAmbiguousParameters = getAmbiguousParameters(); + // omit 'new Type[] {}' for the last parameter of a vararg method call + if (lstParameters.size() == descriptor.params.length && isVarArgCall()) { + Exprent lastParam = lstParameters.get(lstParameters.size() - 1); + if (lastParam.type == EXPRENT_NEW && lastParam.getExprType().arrayDim >= 1) { + ((NewExprent) lastParam).setVarArgParam(true); + } + } + boolean firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.size(); i++) { if (sigFields == null || sigFields.get(i) == null) { - if (!firstParameter) { - buf.append(", "); - } - TextBuffer buff = new TextBuffer(); boolean ambiguous = setAmbiguousParameters.get(i); @@ -361,7 +365,14 @@ public class InvocationExprent extends Exprent { } // 'byte' and 'short' literals need an explicit narrowing type cast when used as a parameter ExprProcessor.getCastedExprent(param, descriptor.params[i], buff, indent, true, ambiguous, true, tracer); - buf.append(buff); + + // the last "new Object[0]" in the vararg call is not printed + if (buff.length() > 0) { + if (!firstParameter) { + buf.append(", "); + } + buf.append(buff); + } firstParameter = false; } @@ -372,6 +383,20 @@ public class InvocationExprent extends Exprent { return buf; } + private boolean isVarArgCall() { + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + if (cl != null) { + StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor)); + if (mt != null) { + return mt.hasModifier(CodeConstants.ACC_VARARGS); + } + } + else { + // TODO: try to check the class on the classpath + } + return false; + } + private boolean isBoxingCall() { if (isStatic && "valueOf".equals(name) && lstParameters.size() == 1) { int paramType = lstParameters.get(0).getExprType().type; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index f31f3a8..d608be4 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -44,6 +44,7 @@ public class NewExprent extends Exprent { private List lstDims = new ArrayList<>(); private List lstArrayElements = new ArrayList<>(); private boolean directArrayInit; + private boolean isVarArgParam; private boolean anonymous; private boolean lambda; private boolean enumConst; @@ -349,6 +350,30 @@ public class NewExprent extends Exprent { } } } + else if (isVarArgParam) { + // just print the array elements + VarType leftType = newType.decreaseArrayDim(); + for (int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + + // new String[][]{{"abc"}, {"DEF"}} => new String[]{"abc"}, new String[]{"DEF"} + Exprent element = lstArrayElements.get(i); + if (element.type == EXPRENT_NEW) { + ((NewExprent) element).setDirectArrayInit(false); + } + ExprProcessor.getCastedExprent(element, leftType, buf, indent, false, tracer); + } + + // if there is just one element of Object[] type it needs to be casted to resolve ambiguity + if (lstArrayElements.size() == 1) { + VarType elementType = lstArrayElements.get(0).getExprType(); + if (elementType.type == CodeConstants.TYPE_OBJECT && elementType.value.equals("java/lang/Object") && elementType.arrayDim >= 1) { + buf.prepend("(Object)"); + } + } + } else { buf.append("new ").append(ExprProcessor.getTypeName(newType)); @@ -478,6 +503,10 @@ public class NewExprent extends Exprent { this.directArrayInit = directArrayInit; } + public void setVarArgParam(boolean isVarArgParam) { + this.isVarArgParam = isVarArgParam; + } + public boolean isLambda() { return lambda; } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index b4567e0..0ce7f87 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -106,7 +106,7 @@ public class SingleClassesTest { "pkg/SharedName2", "pkg/SharedName3", "pkg/SharedName4", "pkg/NonSharedName", "pkg/TestClashNameParent", "ext/TestClashNameParent","pkg/TestClashNameIface", "ext/TestClashNameIface"); } @Test public void testSwitchOnEnum() { doTest("pkg/TestSwitchOnEnum","pkg/TestSwitchOnEnum$1");} - + @Test public void testVarArgCalls() { doTest("pkg/TestVarArgCalls"); } private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/pkg/TestVarArgCalls.class b/testData/classes/pkg/TestVarArgCalls.class new file mode 100644 index 0000000..ac83648 Binary files /dev/null and b/testData/classes/pkg/TestVarArgCalls.class differ diff --git a/testData/results/TestVarArgCalls.dec b/testData/results/TestVarArgCalls.dec new file mode 100644 index 0000000..736585f --- /dev/null +++ b/testData/results/TestVarArgCalls.dec @@ -0,0 +1,104 @@ +package pkg; + +public class TestVarArgCalls { + public void doSmth() { + this.printAll("Test");// 5 + this.printAll("Test: %s", "abc");// 6 + this.printAll("Test: %s - %s", "abc", "DEF");// 7 + 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 + Object[] data = new Object[]{"Hello"};// 17 + String.format("Test: %s", new Object[]{data});// 18 + String.format("Test: %s", (Object[])data);// 19 + }// 20 + + public void printAll(String fmt, String... params) { + System.out.println(String.format(fmt, (Object[])params));// 23 + }// 24 + + public void printComplex(String fmt, String[]... params) { + System.out.println(String.format(fmt, (Object[])params));// 27 + }// 28 +} + +class 'pkg/TestVarArgCalls' { + method 'doSmth ()V' { + 1 4 + 7 4 + b 5 + 13 5 + 16 5 + 1a 6 + 22 6 + 27 6 + 2a 6 + 2e 7 + 34 7 + 38 8 + 46 8 + 4a 8 + 4e 9 + 5c 9 + 68 9 + 6c 9 + 6f 10 + 71 10 + 75 10 + 79 11 + 81 11 + 83 11 + 87 11 + 8b 12 + 93 12 + 95 12 + 9b 12 + 9e 12 + a8 13 + ab 13 + ac 14 + b6 14 + ba 15 + bd 15 + c0 15 + c4 16 + } + + method 'printAll (Ljava/lang/String;[Ljava/lang/String;)V' { + 0 19 + 5 19 + 8 19 + b 19 + e 20 + } + + method 'printComplex (Ljava/lang/String;[[Ljava/lang/String;)V' { + 0 23 + 5 23 + 8 23 + b 23 + e 24 + } +} + +Lines mapping: +5 <-> 5 +6 <-> 6 +7 <-> 7 +9 <-> 8 +10 <-> 9 +11 <-> 10 +13 <-> 11 +14 <-> 12 +15 <-> 13 +17 <-> 14 +18 <-> 15 +19 <-> 16 +20 <-> 17 +23 <-> 20 +24 <-> 21 +27 <-> 24 +28 <-> 25 diff --git a/testData/src/pkg/TestVarArgCalls.java b/testData/src/pkg/TestVarArgCalls.java new file mode 100644 index 0000000..61c199c --- /dev/null +++ b/testData/src/pkg/TestVarArgCalls.java @@ -0,0 +1,29 @@ +package pkg; + +public class TestVarArgCalls { + public void doSmth() { + printAll("Test"); + printAll("Test: %s", "abc"); + printAll("Test: %s - %s", "abc", "DEF"); + + printComplex("Test"); + printComplex("Test: %[0]s", new String[] { "abc" }); + printComplex("Test: %[0]s - %[0]s", new String[] { "abc" }, new String[] { "DEF" }); + + String.format("Test"); + String.format("Test: %d", 123); + String.format("Test: %d - %s", 123, "DEF"); + + Object[] data = { "Hello" }; + String.format("Test: %s", (Object) data); + String.format("Test: %s", (Object[]) data); + } + + public void printAll(String fmt, String... params) { + System.out.println(String.format(fmt, (Object[]) params)); + } + + public void printComplex(String fmt, String[]... params) { + System.out.println(String.format(fmt, (Object[]) params)); + } +}