From dfd90978c960a9741735c8c01e29b9776d0f8a13 Mon Sep 17 00:00:00 2001 From: Dmitry Cherniachenko Date: Tue, 18 Apr 2017 22:16:52 +0200 Subject: [PATCH] Avoid explicit array creation for vararg parameters --- .../decompiler/exps/InvocationExprent.java | 35 +++++- .../modules/decompiler/exps/NewExprent.java | 29 +++++ .../java/decompiler/SingleClassesTest.java | 2 +- testData/classes/pkg/TestVarArgCalls.class | Bin 0 -> 1484 bytes testData/results/TestVarArgCalls.dec | 104 ++++++++++++++++++ testData/src/pkg/TestVarArgCalls.java | 29 +++++ 6 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 testData/classes/pkg/TestVarArgCalls.class create mode 100644 testData/results/TestVarArgCalls.dec create mode 100644 testData/src/pkg/TestVarArgCalls.java 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 0000000000000000000000000000000000000000..ac83648354ab36de8a64b4683235c82612398d57 GIT binary patch literal 1484 zcmb7EOHL%2QD51MCARDuO&jk%yoKbi&|t7#ArGC73o$35<*@*8U6E zY;=JPGdeDGWp@goAaITobTN4-t^b+vu^-Kup*-g0}=*B49Vy~TEeh| zyAtk6xG$ogVSgB1FyDo4U2GmrWk~V941v_ZZqUyKf8WHDk}A{7ktp zt6SFIB=yeQTG832u6f!_7csA30ne&Rd0#2KXjqmlVo|{oo{M;)AcJKRs&Uff z_?BHR>a?@*T4n~$$UpQ%0IHlzYt@ydl4BH%GJ|-aTl>c97DGpTDOlw*==uaUsU7Y) zMv=_3_i2`PPsFs-FZl%{9YZftLDbc`2$Pa~E36XL)!0K~oldBOK0f{nf$`&{XE#M@ zS{be3|aZ&^XD z*W6GHpNH9yDtQ^_Y@)?rG8LnE>DVkra5lS>=g_ifH zXwh7xm5M8ftI1)Hrm9-T9gqkqj~-5aN0c+Fn;&(Z3PUCvx*=kawmC-8G8$o1?hwuR zfCxUK6~|C 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)); + } +}