Avoid explicit array creation for vararg parameters

master
Dmitry Cherniachenko 7 years ago committed by Egor.Ushakov
parent d149b53799
commit dfd90978c9
  1. 35
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java
  2. 29
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java
  3. 2
      test/org/jetbrains/java/decompiler/SingleClassesTest.java
  4. BIN
      testData/classes/pkg/TestVarArgCalls.class
  5. 104
      testData/results/TestVarArgCalls.dec
  6. 29
      testData/src/pkg/TestVarArgCalls.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;

@ -44,6 +44,7 @@ public class NewExprent extends Exprent {
private List<Exprent> lstDims = new ArrayList<>();
private List<Exprent> 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;
}

@ -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();

@ -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

@ -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));
}
}
Loading…
Cancel
Save