Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorssankaran2014-03-12 04:46:47 +0000
committerssankaran2014-03-12 05:13:08 +0000
commit2323f3f57de6c106bba7d022fda77960201afa72 (patch)
tree7335e460eb082b3c2093dd59d24342eeea432196
parent9916f775bcfe4fab4e68ad17a0d64a5b39bb1037 (diff)
downloadeclipse.jdt.core-2323f3f57de6c106bba7d022fda77960201afa72.tar.gz
eclipse.jdt.core-2323f3f57de6c106bba7d022fda77960201afa72.tar.xz
eclipse.jdt.core-2323f3f57de6c106bba7d022fda77960201afa72.zip
Fixed Bug 430035 - [1.8][compiler][codegen] Bridge methods are not
generated for lambdas/method references
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JSR335ClassFileTest.java229
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java185
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java27
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java78
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java3
8 files changed, 523 insertions, 11 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JSR335ClassFileTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JSR335ClassFileTest.java
index cf9791b098..80dd922ab4 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JSR335ClassFileTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JSR335ClassFileTest.java
@@ -2817,6 +2817,235 @@ public void test430015a() throws IOException, ClassFormatException {
verifyClassFile(expectedOutput, "X.class", ClassFileBytesDisassembler.SYSTEM);
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035() throws IOException, ClassFormatException {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " void foo(String t, T u);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " void foo(T t, String u);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ " void foo(String t, String u);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = (s, u) -> System.out.println(\"m(\"+ s + u + ')');\n" +
+ " k.foo(\"direct\", \" call\");\n" +
+ " J<String> j = k;\n" +
+ " j.foo(\"bridge\", \" method(j)\");\n" +
+ " I<String> i = k;\n" +
+ " i.foo(\"bridge\", \" method(i)\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method(j))\n" +
+ "m(bridge method(i))");
+
+ String expectedOutput =
+ "// Compiled from X.java (version 1.8 : 52.0, super bit)\n" +
+ "public class X {\n" +
+ " Constant pool:\n" +
+ " constant #1 class: #2 X\n" +
+ " constant #2 utf8: \"X\"\n" +
+ " constant #3 class: #4 java/lang/Object\n" +
+ " constant #4 utf8: \"java/lang/Object\"\n" +
+ " constant #5 utf8: \"<init>\"\n" +
+ " constant #6 utf8: \"()V\"\n" +
+ " constant #7 utf8: \"Code\"\n" +
+ " constant #8 method_ref: #3.#9 java/lang/Object.<init> ()V\n" +
+ " constant #9 name_and_type: #5.#6 <init> ()V\n" +
+ " constant #10 utf8: \"LineNumberTable\"\n" +
+ " constant #11 utf8: \"LocalVariableTable\"\n" +
+ " constant #12 utf8: \"this\"\n" +
+ " constant #13 utf8: \"LX;\"\n" +
+ " constant #14 utf8: \"main\"\n" +
+ " constant #15 utf8: \"([Ljava/lang/String;)V\"\n" +
+ " constant #16 name_and_type: #17.#18 foo ()LK;\n" +
+ " constant #17 utf8: \"foo\"\n" +
+ " constant #18 utf8: \"()LK;\"\n" +
+ " constant #19 invoke dynamic: #0 #16 foo ()LK;\n" +
+ " constant #20 string: #21 \"direct\"\n" +
+ " constant #21 utf8: \"direct\"\n" +
+ " constant #22 string: #23 \" call\"\n" +
+ " constant #23 utf8: \" call\"\n" +
+ " constant #24 interface_method_ref: #25.#27 K.foo (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #25 class: #26 K\n" +
+ " constant #26 utf8: \"K\"\n" +
+ " constant #27 name_and_type: #17.#28 foo (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #28 utf8: \"(Ljava/lang/String;Ljava/lang/String;)V\"\n" +
+ " constant #29 string: #30 \"bridge\"\n" +
+ " constant #30 utf8: \"bridge\"\n" +
+ " constant #31 string: #32 \" method(j)\"\n" +
+ " constant #32 utf8: \" method(j)\"\n" +
+ " constant #33 interface_method_ref: #34.#36 J.foo (Ljava/lang/Object;Ljava/lang/String;)V\n" +
+ " constant #34 class: #35 J\n" +
+ " constant #35 utf8: \"J\"\n" +
+ " constant #36 name_and_type: #17.#37 foo (Ljava/lang/Object;Ljava/lang/String;)V\n" +
+ " constant #37 utf8: \"(Ljava/lang/Object;Ljava/lang/String;)V\"\n" +
+ " constant #38 string: #39 \" method(i)\"\n" +
+ " constant #39 utf8: \" method(i)\"\n" +
+ " constant #40 interface_method_ref: #41.#43 I.foo (Ljava/lang/String;Ljava/lang/Object;)V\n" +
+ " constant #41 class: #42 I\n" +
+ " constant #42 utf8: \"I\"\n" +
+ " constant #43 name_and_type: #17.#44 foo (Ljava/lang/String;Ljava/lang/Object;)V\n" +
+ " constant #44 utf8: \"(Ljava/lang/String;Ljava/lang/Object;)V\"\n" +
+ " constant #45 utf8: \"x\"\n" +
+ " constant #46 utf8: \"[Ljava/lang/String;\"\n" +
+ " constant #47 utf8: \"k\"\n" +
+ " constant #48 utf8: \"LK;\"\n" +
+ " constant #49 utf8: \"j\"\n" +
+ " constant #50 utf8: \"LJ;\"\n" +
+ " constant #51 utf8: \"i\"\n" +
+ " constant #52 utf8: \"LI;\"\n" +
+ " constant #53 utf8: \"LocalVariableTypeTable\"\n" +
+ " constant #54 utf8: \"LJ<Ljava/lang/String;>;\"\n" +
+ " constant #55 utf8: \"LI<Ljava/lang/String;>;\"\n" +
+ " constant #56 utf8: \"lambda$0\"\n" +
+ " constant #57 field_ref: #58.#60 java/lang/System.out Ljava/io/PrintStream;\n" +
+ " constant #58 class: #59 java/lang/System\n" +
+ " constant #59 utf8: \"java/lang/System\"\n" +
+ " constant #60 name_and_type: #61.#62 out Ljava/io/PrintStream;\n" +
+ " constant #61 utf8: \"out\"\n" +
+ " constant #62 utf8: \"Ljava/io/PrintStream;\"\n" +
+ " constant #63 class: #64 java/lang/StringBuilder\n" +
+ " constant #64 utf8: \"java/lang/StringBuilder\"\n" +
+ " constant #65 string: #66 \"m(\"\n" +
+ " constant #66 utf8: \"m(\"\n" +
+ " constant #67 method_ref: #63.#68 java/lang/StringBuilder.<init> (Ljava/lang/String;)V\n" +
+ " constant #68 name_and_type: #5.#69 <init> (Ljava/lang/String;)V\n" +
+ " constant #69 utf8: \"(Ljava/lang/String;)V\"\n" +
+ " constant #70 method_ref: #63.#71 java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;\n" +
+ " constant #71 name_and_type: #72.#73 append (Ljava/lang/String;)Ljava/lang/StringBuilder;\n" +
+ " constant #72 utf8: \"append\"\n" +
+ " constant #73 utf8: \"(Ljava/lang/String;)Ljava/lang/StringBuilder;\"\n" +
+ " constant #74 method_ref: #63.#75 java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;\n" +
+ " constant #75 name_and_type: #72.#76 append (C)Ljava/lang/StringBuilder;\n" +
+ " constant #76 utf8: \"(C)Ljava/lang/StringBuilder;\"\n" +
+ " constant #77 method_ref: #63.#78 java/lang/StringBuilder.toString ()Ljava/lang/String;\n" +
+ " constant #78 name_and_type: #79.#80 toString ()Ljava/lang/String;\n" +
+ " constant #79 utf8: \"toString\"\n" +
+ " constant #80 utf8: \"()Ljava/lang/String;\"\n" +
+ " constant #81 method_ref: #82.#84 java/io/PrintStream.println (Ljava/lang/String;)V\n" +
+ " constant #82 class: #83 java/io/PrintStream\n" +
+ " constant #83 utf8: \"java/io/PrintStream\"\n" +
+ " constant #84 name_and_type: #85.#69 println (Ljava/lang/String;)V\n" +
+ " constant #85 utf8: \"println\"\n" +
+ " constant #86 utf8: \"s\"\n" +
+ " constant #87 utf8: \"Ljava/lang/String;\"\n" +
+ " constant #88 utf8: \"u\"\n" +
+ " constant #89 utf8: \"SourceFile\"\n" +
+ " constant #90 utf8: \"X.java\"\n" +
+ " constant #91 utf8: \"BootstrapMethods\"\n" +
+ " constant #92 method_ref: #93.#95 java/lang/invoke/LambdaMetafactory.altMetafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n" +
+ " constant #93 class: #94 java/lang/invoke/LambdaMetafactory\n" +
+ " constant #94 utf8: \"java/lang/invoke/LambdaMetafactory\"\n" +
+ " constant #95 name_and_type: #96.#97 altMetafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\n" +
+ " constant #96 utf8: \"altMetafactory\"\n" +
+ " constant #97 utf8: \"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;\"\n" +
+ " constant #98 method handle: invokestatic (6) #92 \n" +
+ " constant #99 method type: #28 (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #100 method_ref: #1.#101 X.lambda$0 (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #101 name_and_type: #56.#28 lambda$0 (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #102 method handle: invokestatic (6) #100 \n" +
+ " constant #103 method type: #28 (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " constant #104 integer: 4\n" + // flag bridge
+ " constant #105 integer: 2\n" + // two bridges
+ " constant #106 method type: #44 (Ljava/lang/String;Ljava/lang/Object;)V\n" + // first bridge
+ " constant #107 method type: #37 (Ljava/lang/Object;Ljava/lang/String;)V\n" + // next bridge.
+ " constant #108 utf8: \"InnerClasses\"\n" +
+ " constant #109 class: #110 java/lang/invoke/MethodHandles$Lookup\n" +
+ " constant #110 utf8: \"java/lang/invoke/MethodHandles$Lookup\"\n" +
+ " constant #111 class: #112 java/lang/invoke/MethodHandles\n" +
+ " constant #112 utf8: \"java/lang/invoke/MethodHandles\"\n" +
+ " constant #113 utf8: \"Lookup\"\n" +
+ " \n" +
+ " // Method descriptor #6 ()V\n" +
+ " // Stack: 1, Locals: 1\n" +
+ " public X();\n" +
+ " 0 aload_0 [this]\n" +
+ " 1 invokespecial java.lang.Object() [8]\n" +
+ " 4 return\n" +
+ " Line numbers:\n" +
+ " [pc: 0, line: 10]\n" +
+ " Local variable table:\n" +
+ " [pc: 0, pc: 5] local: this index: 0 type: X\n" +
+ " \n" +
+ " // Method descriptor #15 ([Ljava/lang/String;)V\n" +
+ " // Stack: 3, Locals: 4\n" +
+ " public static void main(java.lang.String... x);\n" +
+ " 0 invokedynamic 0 foo() : K [19]\n" +
+ " 5 astore_1 [k]\n" +
+ " 6 aload_1 [k]\n" +
+ " 7 ldc <String \"direct\"> [20]\n" +
+ " 9 ldc <String \" call\"> [22]\n" +
+ " 11 invokeinterface K.foo(java.lang.String, java.lang.String) : void [24] [nargs: 3]\n" +
+ " 16 aload_1 [k]\n" +
+ " 17 astore_2 [j]\n" +
+ " 18 aload_2 [j]\n" +
+ " 19 ldc <String \"bridge\"> [29]\n" +
+ " 21 ldc <String \" method(j)\"> [31]\n" +
+ " 23 invokeinterface J.foo(java.lang.Object, java.lang.String) : void [33] [nargs: 3]\n" +
+ " 28 aload_1 [k]\n" +
+ " 29 astore_3 [i]\n" +
+ " 30 aload_3 [i]\n" +
+ " 31 ldc <String \"bridge\"> [29]\n" +
+ " 33 ldc <String \" method(i)\"> [38]\n" +
+ " 35 invokeinterface I.foo(java.lang.String, java.lang.Object) : void [40] [nargs: 3]\n" +
+ " 40 return\n" +
+ " Line numbers:\n" +
+ " [pc: 0, line: 12]\n" +
+ " [pc: 6, line: 13]\n" +
+ " [pc: 16, line: 14]\n" +
+ " [pc: 18, line: 15]\n" +
+ " [pc: 28, line: 16]\n" +
+ " [pc: 30, line: 17]\n" +
+ " [pc: 40, line: 18]\n" +
+ " Local variable table:\n" +
+ " [pc: 0, pc: 41] local: x index: 0 type: java.lang.String[]\n" +
+ " [pc: 6, pc: 41] local: k index: 1 type: K\n" +
+ " [pc: 18, pc: 41] local: j index: 2 type: J\n" +
+ " [pc: 30, pc: 41] local: i index: 3 type: I\n" +
+ " Local variable type table:\n" +
+ " [pc: 18, pc: 41] local: j index: 2 type: J<java.lang.String>\n" +
+ " [pc: 30, pc: 41] local: i index: 3 type: I<java.lang.String>\n" +
+ " \n" +
+ " // Method descriptor #28 (Ljava/lang/String;Ljava/lang/String;)V\n" +
+ " // Stack: 4, Locals: 2\n" +
+ " private static synthetic void lambda$0(java.lang.String s, java.lang.String u);\n" +
+ " 0 getstatic java.lang.System.out : java.io.PrintStream [57]\n" +
+ " 3 new java.lang.StringBuilder [63]\n" +
+ " 6 dup\n" +
+ " 7 ldc <String \"m(\"> [65]\n" +
+ " 9 invokespecial java.lang.StringBuilder(java.lang.String) [67]\n" +
+ " 12 aload_0 [s]\n" +
+ " 13 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [70]\n" +
+ " 16 aload_1 [u]\n" +
+ " 17 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [70]\n" +
+ " 20 bipush 41\n" +
+ " 22 invokevirtual java.lang.StringBuilder.append(char) : java.lang.StringBuilder [74]\n" +
+ " 25 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [77]\n" +
+ " 28 invokevirtual java.io.PrintStream.println(java.lang.String) : void [81]\n" +
+ " 31 return\n" +
+ " Line numbers:\n" +
+ " [pc: 0, line: 12]\n" +
+ " Local variable table:\n" +
+ " [pc: 0, pc: 32] local: s index: 0 type: java.lang.String\n" +
+ " [pc: 0, pc: 32] local: u index: 1 type: java.lang.String\n" +
+ "\n" +
+ " Inner classes:\n" +
+ " [inner class info: #109 java/lang/invoke/MethodHandles$Lookup, outer class info: #111 java/lang/invoke/MethodHandles\n" +
+ " inner name: #113 Lookup, accessflags: 25 public static final]\n" +
+ "Bootstrap methods:\n" +
+ " 0 : # 98 arguments: {#99,#102,#103,#104,#105,#106,#107}\n" +
+ "}";
+
+ verifyClassFile(expectedOutput, "X.class", ClassFileBytesDisassembler.SYSTEM);
+}
public static Class testClass() {
return JSR335ClassFileTest.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
index b487b5d09c..e6e8ba306d 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
@@ -3811,6 +3811,191 @@ public void test430043() {
},
"OK");
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "import java.util.function.Consumer;\n" +
+ "public class X {\n" +
+ " interface StringConsumer extends Consumer<String> {\n" +
+ " void accept(String t);\n" +
+ " }\n" +
+ " public static void main(String... x) {\n" +
+ " StringConsumer c = s->System.out.println(\"m(\"+s+')');\n" +
+ " c.accept(\"direct call\");\n" +
+ " Consumer<String> c4b=c;\n" +
+ " c4b.accept(\"bridge method\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method)");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035a() { // test reference expressions requiring bridges.
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "import java.util.function.Consumer;\n" +
+ "public class X {\n" +
+ " interface StringConsumer extends Consumer<String> {\n" +
+ " void accept(String t);\n" +
+ " }\n" +
+ " static void m(String s) { System.out.println(\"m(\"+s+\")\"); } \n" +
+ " public static void main(String... x) {\n" +
+ " StringConsumer c = X::m;\n" +
+ " c.accept(\"direct call\");\n" +
+ " Consumer<String> c4b=c;\n" +
+ " c4b.accept(\"bridge method\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method)");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035b() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " void foo(T t);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " void foo(T t);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = s -> System.out.println(\"m(\"+s+')');\n" +
+ " k.foo(\"direct call\");\n" +
+ " J<String> j = k;\n" +
+ " j.foo(\"bridge method\");\n" +
+ " I<String> i = k;\n" +
+ " i.foo(\"bridge method\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method)\n" +
+ "m(bridge method)");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035c() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " void foo(String t, T u);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " void foo(T t, String u);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ " void foo(String t, String u);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = (s, u) -> System.out.println(\"m(\"+ s + u + ')');\n" +
+ " k.foo(\"direct\", \" call\");\n" +
+ " J<String> j = k;\n" +
+ " j.foo(\"bridge\", \" method(j)\");\n" +
+ " I<String> i = k;\n" +
+ " i.foo(\"bridge\", \" method(i)\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method(j))\n" +
+ "m(bridge method(i))");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035d() { // 8b131 complains of ambiguity.
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " void foo(String t, T u);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " void foo(T t, String u);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = (s, u) -> System.out.println(\"m(\"+ s + u + ')');\n" +
+ " k.foo(\"direct\", \" call\");\n" +
+ " J<String> j = k;\n" +
+ " j.foo(\"bridge\", \" method(j)\");\n" +
+ " I<String> i = k;\n" +
+ " i.foo(\"bridge\", \" method(i)\");\n" +
+ " }\n" +
+ "}\n"
+ },
+ "m(direct call)\n" +
+ "m(bridge method(j))\n" +
+ "m(bridge method(i))");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035e() { // 8b131 complains of ambiguity in call.
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " Object foo(String t, T u);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " String foo(T t, String u);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = (s, u) -> s + u;\n" +
+ " System.out.println(k.foo(\"direct\", \" call\"));\n" +
+ " J<String> j = k;\n" +
+ " System.out.println(j.foo(\"bridge\", \" method(j)\"));\n" +
+ " I<String> i = k;\n" +
+ " System.out.println(i.foo(\"bridge\", \" method(i)\"));\n" +
+ " }\n" +
+ "}\n"
+ },
+ "direct call\n" +
+ "bridge method(j)\n" +
+ "bridge method(i)");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430035, [1.8][compiler][codegen] Bridge methods are not generated for lambdas/method references
+public void test430035f() { // ensure co-variant return emits a bridge request.
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "interface I<T> {\n" +
+ " Object foo(String t, String u);\n" +
+ "}\n" +
+ "interface J<T> {\n" +
+ " String foo(String t, String u);\n" +
+ "}\n" +
+ "interface K extends I<String>, J<String> {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String... x) {\n" +
+ " K k = (s, u) -> s + u;\n" +
+ " System.out.println(k.foo(\"direct\", \" call\"));\n" +
+ " J<String> j = k;\n" +
+ " System.out.println(j.foo(\"bridge\", \" method(j)\"));\n" +
+ " I<String> i = k;\n" +
+ " System.out.println(i.foo(\"bridge\", \" method(i)\"));\n" +
+ " }\n" +
+ "}\n"
+ },
+ "direct call\n" +
+ "bridge method(j)\n" +
+ "bridge method(i)");
+}
+
public static Class testClass() {
return LambdaExpressionsTest.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
index 7e70e425f2..3727dbc456 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
@@ -1699,7 +1699,7 @@ public void test044() {
"5. ERROR in X.java (at line 11)\n" +
" D d = (p) -> { return null;};\n" +
" ^^^^^^\n" +
- "Illegal lambda expression: Method foo of type A is generic \n" +
+ "Illegal lambda expression: Method foo of type B is generic \n" +
"----------\n" +
"6. ERROR in X.java (at line 12)\n" +
" E e = (p) -> { return null;};\n" +
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
index bf7e30ce4c..f2046bd943 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
@@ -2880,11 +2880,12 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contents[localContentsOffset++] = (byte) numberOfBootstraps;
for (int i = 0; i < numberOfBootstraps; i++) {
FunctionalExpression functional = (FunctionalExpression) functionalExpressionList.get(i);
-
+ MethodBinding [] bridges = functional.getRequiredBridges();
TypeBinding[] markerInterfaces = null;
if (functional instanceof LambdaExpression &&
(((markerInterfaces=((LambdaExpression)functional).getMarkerInterfaces()) != null) ||
- ((LambdaExpression)functional).isSerializable)) {
+ ((LambdaExpression)functional).isSerializable) ||
+ bridges != null) {
LambdaExpression lambdaEx = (LambdaExpression)functional;
// may need even more space
@@ -2893,6 +2894,10 @@ public class ClassFile implements TypeConstants, TypeIds {
// 2 for the marker interface list size then 2 per marker interface index
extraSpace += (2 + 2 * markerInterfaces.length);
}
+ if (bridges != null) {
+ // 2 for bridge count then 2 per bridge method type.
+ extraSpace += (2 + 2 * bridges.length);
+ }
if (extraSpace + localContentsOffset >= this.contents.length) {
resizeContents(extraSpace);
}
@@ -2907,7 +2912,8 @@ public class ClassFile implements TypeConstants, TypeIds {
// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
- this.contents[localContentsOffset++] = (byte) (4+(markerInterfaces==null?0:1+markerInterfaces.length));
+ this.contents[localContentsOffset++] = (byte) (4 + (markerInterfaces==null?0:1+markerInterfaces.length) +
+ (bridges == null ? 0 : 1 + bridges.length));
int functionalDescriptorIndex = this.constantPool.literalIndexForMethodType(functional.descriptor.original().signature());
this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8);
@@ -2922,7 +2928,6 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contents[localContentsOffset++] = (byte) (methodTypeIndex >> 8);
this.contents[localContentsOffset++] = (byte) methodTypeIndex;
- // Does this block have to deal with FLAG_BRIDGE? When is it needed?
int bitflags = 0;
if (lambdaEx.isSerializable) {
bitflags |= ClassFileConstants.FLAG_SERIALIZABLE;
@@ -2930,6 +2935,9 @@ public class ClassFile implements TypeConstants, TypeIds {
if (markerInterfaces!=null) {
bitflags |= ClassFileConstants.FLAG_MARKERS;
}
+ if (bridges != null) {
+ bitflags |= ClassFileConstants.FLAG_BRIDGES;
+ }
int indexForBitflags = this.constantPool.literalIndex(bitflags);
this.contents[localContentsOffset++] = (byte)(indexForBitflags>>8);
@@ -2945,6 +2953,17 @@ public class ClassFile implements TypeConstants, TypeIds {
this.contents[localContentsOffset++] = (byte)(classTypeIndex);
}
}
+ if (bridges != null) {
+ int bridgeCountIndex = this.constantPool.literalIndex(bridges.length);
+ this.contents[localContentsOffset++] = (byte) (bridgeCountIndex >> 8);
+ this.contents[localContentsOffset++] = (byte) (bridgeCountIndex);
+ for (int m = 0, maxm = bridges.length; m < maxm; m++) {
+ char [] bridgeSignature = bridges[m].signature();
+ int bridgeMethodTypeIndex = this.constantPool.literalIndexForMethodType(bridgeSignature);
+ this.contents[localContentsOffset++] = (byte) (bridgeMethodTypeIndex >> 8);
+ this.contents[localContentsOffset++] = (byte) bridgeMethodTypeIndex;
+ }
+ }
} else {
if (indexForMetaFactory == 0) {
indexForMetaFactory = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, javaLangInvokeLambdaMetafactory,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
index df3300c655..39f06b440b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
@@ -39,8 +39,11 @@ import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.IntersectionCastTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
@@ -168,6 +171,7 @@ public abstract class FunctionalExpression extends Expression {
public TypeBinding resolveType(BlockScope blockScope) {
this.constant = Constant.NotAConstant;
+ this.enclosingScope = blockScope;
MethodBinding sam = this.expectedType == null ? null : this.expectedType.getSingleAbstractMethod(blockScope, argumentsTypeElided());
if (sam == null) {
blockScope.problemReporter().targetTypeIsNotAFunctionalInterface(this);
@@ -282,4 +286,76 @@ public abstract class FunctionalExpression extends Expression {
public int diagnosticsSourceEnd() {
return this.sourceEnd;
}
-}
+
+ public MethodBinding[] getRequiredBridges() {
+
+ class BridgeCollector {
+
+ MethodBinding [] bridges;
+ MethodBinding method;
+ char [] selector;
+ LookupEnvironment environment;
+ Scope scope;
+
+ BridgeCollector(ReferenceBinding functionalType, MethodBinding method) {
+ this.method = method;
+ this.selector = method.selector;
+ this.environment = FunctionalExpression.this.enclosingScope.environment();
+ this.scope = FunctionalExpression.this.enclosingScope;
+ collectBridges(functionalType.superInterfaces());
+ }
+
+ void collectBridges(ReferenceBinding[] interfaces) {
+ int length = interfaces == null ? 0 : interfaces.length;
+ for (int i = 0; i < length; i++) {
+ ReferenceBinding superInterface = interfaces[i];
+ if (superInterface == null)
+ continue;
+ MethodBinding [] methods = superInterface.getMethods(this.selector);
+ for (int j = 0, count = methods == null ? 0 : methods.length; j < count; j++) {
+ MethodBinding inheritedMethod = methods[j];
+ if (inheritedMethod == null || this.method == inheritedMethod) // descriptor declaring class may not be same functional interface target type.
+ continue;
+ if (inheritedMethod.isStatic() || inheritedMethod.isDefaultMethod() || inheritedMethod.redeclaresPublicObjectMethod(this.scope))
+ continue;
+ inheritedMethod = MethodVerifier.computeSubstituteMethod(inheritedMethod, this.method, this.environment);
+ if (inheritedMethod == null || !MethodVerifier.isSubstituteParameterSubsignature(this.method, inheritedMethod, this.environment) ||
+ !MethodVerifier.areReturnTypesCompatible(this.method, inheritedMethod, this.environment))
+ continue;
+ final MethodBinding originalInherited = inheritedMethod.original();
+ if (!this.method.areParameterErasuresEqual(originalInherited) || TypeBinding.notEquals(this.method.returnType, originalInherited.returnType))
+ add(originalInherited);
+ }
+ collectBridges(superInterface.superInterfaces());
+ }
+ }
+ void add(MethodBinding inheritedMethod) {
+ if (this.bridges == null) {
+ this.bridges = new MethodBinding[] { inheritedMethod };
+ return;
+ }
+ int length = this.bridges.length;
+ for (int i = 0; i < length; i++) {
+ if (this.bridges[i].areParameterErasuresEqual(inheritedMethod) && TypeBinding.equalsEquals(this.bridges[i].returnType, inheritedMethod.returnType))
+ return;
+ }
+ System.arraycopy(this.bridges, 0, this.bridges = new MethodBinding[length + 1], 0, length);
+ this.bridges[length] = inheritedMethod;
+ }
+ MethodBinding [] getBridges () {
+ return this.bridges;
+ }
+ }
+
+ ReferenceBinding functionalType;
+ if (this.expectedType instanceof IntersectionCastTypeBinding) {
+ functionalType = (ReferenceBinding) ((IntersectionCastTypeBinding)this.expectedType).getSAMType(this.enclosingScope);
+ } else {
+ functionalType = (ReferenceBinding) this.expectedType;
+ }
+ return new BridgeCollector(functionalType, this.descriptor).getBridges();
+ }
+ boolean requiresBridges() {
+ return getRequiredBridges() != null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 53265a6684..f45230b105 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -184,7 +184,9 @@ public class ReferenceExpression extends FunctionalExpression implements Invocat
return (this.binding.isVarargs() ||
(isConstructorReference() && this.receiverType.syntheticOuterLocalVariables() != null && currentScope.methodScope().isStatic) ||
this.expectedType instanceof IntersectionCastTypeBinding || // marker interfaces require alternate meta factory.
- this.expectedType.findSuperTypeOriginatingFrom(currentScope.getJavaIoSerializable()) != null); // serialization support.
+ this.expectedType.findSuperTypeOriginatingFrom(currentScope.getJavaIoSerializable()) != null || // serialization support.
+ this.requiresBridges()); // bridges.
+ // To fix: We should opt for direct code generation wherever possible.
}
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
index 05162f39d3..86e7b30728 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
@@ -83,7 +83,7 @@ static boolean areMethodsCompatible(MethodBinding one, MethodBinding two, Lookup
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) {
return areReturnTypesCompatible(one, two, this.type.scope.environment());
}
-static boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two, LookupEnvironment environment) {
+public static boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two, LookupEnvironment environment) {
if (TypeBinding.equalsEquals(one.returnType, two.returnType)) return true;
if (environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_5) {
// short is compatible with int, but as far as covariance is concerned, its not
@@ -667,7 +667,7 @@ MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBindi
return computeSubstituteMethod(inheritedMethod, currentMethod, this.environment);
}
-static MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod, LookupEnvironment environment) {
+public static MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod, LookupEnvironment environment) {
if (inheritedMethod == null) return null;
if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match
@@ -857,7 +857,7 @@ boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding su
return isSubstituteParameterSubsignature(method, substituteMethod, this.environment);
}
-static boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding substituteMethod, LookupEnvironment environment) {
+public static boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding substituteMethod, LookupEnvironment environment) {
if (!areParametersEqual(method, substituteMethod)) {
// method can still override substituteMethod in cases like :
// <U extends Number> void c(U u) {}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 39137a2442..75933ff21e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -1960,7 +1960,8 @@ public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcar
final LookupEnvironment environment = scope.environment();
boolean genericMethodSeen = false;
- next:for (int i = 0, length = methods.length; i < length; i++) {
+ int length = methods.length;
+ next:for (int i = length - 1; i >= 0; --i) {
MethodBinding method = methods[i], otherMethod = null;
if (method.typeVariables != Binding.NO_TYPE_VARIABLES)
genericMethodSeen = true;

Back to the top