java-decompiler: extra casts on method calls omitted
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Tue, 4 Nov 2014 10:30:23 +0000 (11:30 +0100)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Tue, 4 Nov 2014 10:32:40 +0000 (11:32 +0100)
(loosely based on https://github.com/JetBrains/intellij-community/pull/217)

plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java
plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java
plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java
plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCall.class [new file with mode: 0644]
plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCallWithDebugInfo.class [new file with mode: 0644]
plugins/java-decompiler/engine/testData/results/TestAmbiguousCall.dec [new file with mode: 0644]
plugins/java-decompiler/engine/testData/results/TestAmbiguousCallWithDebugInfo.dec [new file with mode: 0644]
plugins/java-decompiler/engine/testData/results/TestEnum.dec
plugins/java-decompiler/engine/testData/src/pkg/TestAmbiguousCall.java [new file with mode: 0644]

index b80b745c04e2b88868d51740e5ab99a921fc0cc5..d7d2d76020c4f513b9a013357d219f9160d4781f 100644 (file)
@@ -53,6 +53,8 @@ public class InvocationExprent extends Exprent {
   public static final int CONSTRUCTOR_THIS = 1;
   public static final int CONSTRUCTOR_SUPER = 2;
 
+  private static final BitSet EMPTY_BIT_SET = new BitSet(0);
+
   private String name;
 
   private String classname;
@@ -321,7 +323,7 @@ public class InvocationExprent extends Exprent {
 
         break;
       case TYP_CLINIT:
-        throw new RuntimeException("Explicite invocation of <clinit>");
+        throw new RuntimeException("Explicit invocation of <clinit>");
       case TYP_INIT:
         if (super_qualifier != null) {
           buf.append("super(");
@@ -355,7 +357,7 @@ public class InvocationExprent extends Exprent {
       }
     }
 
-    Set<Integer> setAmbiguousParameters = getAmbiguousParameters();
+    BitSet setAmbiguousParameters = getAmbiguousParameters();
 
     boolean firstParameter = true;
     int start = isEnum ? 2 : 0;
@@ -366,7 +368,7 @@ public class InvocationExprent extends Exprent {
         }
 
         TextBuffer buff = new TextBuffer();
-        boolean ambiguous = setAmbiguousParameters.contains(i);
+        boolean ambiguous = setAmbiguousParameters.get(i);
         ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, tracer);
         buf.append(buff);
 
@@ -379,47 +381,56 @@ public class InvocationExprent extends Exprent {
     return buf;
   }
 
-  private Set<Integer> getAmbiguousParameters() {
-
-    Set<Integer> ret = new HashSet<Integer>();
-
-    StructClass cstr = DecompilerContext.getStructContext().getClass(classname);
-    if (cstr != null) {
-      List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>();
-      for (StructMethod meth : cstr.getMethods()) {
-        if (name.equals(meth.getName())) {
-          MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor());
-          if (md.params.length == descriptor.params.length) {
-            boolean equals = true;
-            for (int i = 0; i < md.params.length; i++) {
-              if (md.params[i].type_family != descriptor.params[i].type_family) {
-                equals = false;
-                break;
-              }
-            }
-
-            if (equals) {
-              lstMethods.add(md);
+  private BitSet getAmbiguousParameters() {
+    StructClass cl = DecompilerContext.getStructContext().getClass(classname);
+    if (cl == null) return EMPTY_BIT_SET;
+
+    // check number of matches
+    List<MethodDescriptor> matches = new ArrayList<MethodDescriptor>();
+    nextMethod:
+    for (StructMethod mt : cl.getMethods()) {
+      if (name.equals(mt.getName())) {
+        MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+        if (md.params.length == descriptor.params.length) {
+          for (int i = 0; i < md.params.length; i++) {
+            if (md.params[i].type_family != descriptor.params[i].type_family) {
+              continue nextMethod;
             }
           }
+          matches.add(md);
         }
       }
-
-      if (lstMethods.size() > 1) {
-        for (int i = 0; i < descriptor.params.length; i++) {
-          VarType partype = descriptor.params[i];
-
-          for (MethodDescriptor md : lstMethods) {
-            if (!partype.equals(md.params[i])) {
-              ret.add(i);
-              break;
-            }
+    }
+    if (matches.size() == 1) return EMPTY_BIT_SET;
+
+    // check if a call is unambiguous
+    StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor));
+    if (mt != null) {
+      MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
+      if (md.params.length == lstParameters.size()) {
+        boolean exact = true;
+        for (int i = 0; i < md.params.length; i++) {
+          if (!md.params[i].equals(lstParameters.get(i).getExprType())) {
+            exact = false;
+            break;
           }
         }
+        if (exact) return EMPTY_BIT_SET;
       }
     }
 
-    return ret;
+    // mark parameters
+    BitSet ambiguous = new BitSet(descriptor.params.length);
+    for (int i = 0; i < descriptor.params.length; i++) {
+      VarType paramType = descriptor.params[i];
+      for (MethodDescriptor md : matches) {
+        if (!paramType.equals(md.params[i])) {
+          ambiguous.set(i);
+          break;
+        }
+      }
+    }
+    return ambiguous;
   }
 
   public boolean equals(Object o) {
index 60e40c013db6351dbcd66f26b22f4aa476cc184e..fceae359d8fa763b162851a43284e5f13b6c13ee 100644 (file)
@@ -35,7 +35,7 @@ import java.util.Map;
 */
 public class StructLocalVariableTableAttribute extends StructGeneralAttribute {
 
-  private Map<Integer, String> mapVarNames = new HashMap<Integer, String>();
+  private Map<Integer, String> mapVarNames = Collections.emptyMap();
 
   @Override
   public void initContent(ConstantPool pool) throws IOException {
index 20d4fa88a329880f22ae68fe7112d7b8557da6e2..c1b36def4c823ddd6a9edf8e518195e0b984d4cc 100644 (file)
@@ -37,4 +37,6 @@ public class SingleClassesTest extends SingleClassesTestBase {
   @Test public void testInnerClassConstructor() { doTest("pkg/TestInnerClassConstructor"); }
   @Test public void testInnerClassConstructor11() { doTest("v11/TestInnerClassConstructor"); }
   @Test public void testTryCatchFinally() { doTest("pkg/TestTryCatchFinally"); }
+  @Test public void testAmbiguousCall() { doTest("pkg/TestAmbiguousCall"); }
+  @Test public void testAmbiguousCallWithDebugInfo() { doTest("pkg/TestAmbiguousCallWithDebugInfo"); }
 }
diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCall.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCall.class
new file mode 100644 (file)
index 0000000..ed5ab9a
Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCall.class differ
diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCallWithDebugInfo.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCallWithDebugInfo.class
new file mode 100644 (file)
index 0000000..5843a61
Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestAmbiguousCallWithDebugInfo.class differ
diff --git a/plugins/java-decompiler/engine/testData/results/TestAmbiguousCall.dec b/plugins/java-decompiler/engine/testData/results/TestAmbiguousCall.dec
new file mode 100644 (file)
index 0000000..b335867
--- /dev/null
@@ -0,0 +1,18 @@
+package pkg;
+
+class TestAmbiguousCall {
+   void m1(RuntimeException var1, String var2) {
+   }
+
+   void m1(IllegalArgumentException var1, String var2) {
+   }
+
+   void test() {
+      IllegalArgumentException var1 = new IllegalArgumentException();
+      this.m1((RuntimeException)var1, "RE");
+      this.m1(var1, "IAE");
+      IllegalArgumentException var2 = new IllegalArgumentException();
+      this.m1((RuntimeException)var2, "RE");
+      this.m1((IllegalArgumentException)var2, "IAE");
+   }
+}
diff --git a/plugins/java-decompiler/engine/testData/results/TestAmbiguousCallWithDebugInfo.dec b/plugins/java-decompiler/engine/testData/results/TestAmbiguousCallWithDebugInfo.dec
new file mode 100644 (file)
index 0000000..da3baa9
--- /dev/null
@@ -0,0 +1,18 @@
+package pkg;
+
+class TestAmbiguousCall {
+   void m1(RuntimeException e, String s) {
+   }
+
+   void m1(IllegalArgumentException e, String s) {
+   }
+
+   void test() {
+      IllegalArgumentException iae = new IllegalArgumentException();
+      this.m1((RuntimeException)iae, "RE");
+      this.m1(iae, "IAE");
+      IllegalArgumentException re = new IllegalArgumentException();
+      this.m1((RuntimeException)re, "RE");
+      this.m1((IllegalArgumentException)re, "IAE");
+   }
+}
index b32a3d9a4f668066feacdbf91caf026d37a3be27..3184741d4043715b1bc793a64540e3e0f927bd79 100644 (file)
@@ -18,7 +18,7 @@ public enum TestEnum {
    }
 
    private TestEnum() {
-      this((String)"?");
+      this("?");
    }
 
    private TestEnum(@Deprecated String var3) {
diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestAmbiguousCall.java b/plugins/java-decompiler/engine/testData/src/pkg/TestAmbiguousCall.java
new file mode 100644 (file)
index 0000000..0189329
--- /dev/null
@@ -0,0 +1,16 @@
+package pkg;
+
+class TestAmbiguousCall {
+  void m1(RuntimeException e, String s) { }
+  void m1(IllegalArgumentException e, String s) { }
+
+  void test() {
+    IllegalArgumentException iae = new IllegalArgumentException();
+    m1((RuntimeException)iae, "RE");
+    m1(iae, "IAE");
+
+    RuntimeException re = new IllegalArgumentException();
+    m1(re, "RE");
+    m1((IllegalArgumentException)re, "IAE");
+  }
+}