8012557: Implement lambda methods on interfaces as private
8016320: Method reference in subinterface of type I.super::foo produces exception at runtime
Summary: Now that the VM supports interface instance private methods, lambda methods and lambda bridges are always private. Access is now through invokespecial.
Reviewed-by: vromero, jlahoda
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu Oct 10 20:57:27 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu Oct 10 23:26:56 2013 -0700
@@ -128,10 +128,9 @@
private KlassInfo(Symbol kSym) {
appendedMethodList = new ListBuffer<>();
deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
- long flags = PRIVATE | STATIC | SYNTHETIC;
MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
List.<Type>nil(), syms.methodClass);
- deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
+ deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, kSym);
deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
syms.serializedLambdaType, deserMethodSym);
}
@@ -671,8 +670,8 @@
/**
* Create new synthetic method with given flags, name, type, owner
*/
- private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
- return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
+ private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
+ return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
}
/**
@@ -1067,12 +1066,12 @@
} else {
if (refSym.isStatic()) {
return ClassFile.REF_invokeStatic;
+ } else if ((refSym.flags() & PRIVATE) != 0) {
+ return ClassFile.REF_invokeSpecial;
} else if (refSym.enclClass().isInterface()) {
return ClassFile.REF_invokeInterface;
} else {
- return (refSym.flags() & PRIVATE) != 0 ?
- ClassFile.REF_invokeSpecial :
- ClassFile.REF_invokeVirtual;
+ return ClassFile.REF_invokeVirtual;
}
}
}
@@ -1480,7 +1479,7 @@
//static clinits are generated in Gen - so we need to fake them
Symbol clinit = clinits.get(csym);
if (clinit == null) {
- clinit = makeSyntheticMethod(STATIC,
+ clinit = makePrivateSyntheticMethod(STATIC,
names.clinit,
new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
csym);
@@ -1729,7 +1728,7 @@
self = ((JCVariableDecl)frame.tree).sym;
}
Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
- this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
+ this.translatedSym = makePrivateSyntheticMethod(0, name, null, owner.enclClass());
if (dumpLambdaToMethodStats) {
log.note(tree, "lambda.stat", needsAltMetafactory(), translatedSym);
}
@@ -1845,9 +1844,9 @@
// If instance access isn't needed, make it static.
// Interface instance methods must be default methods.
- // Awaiting VM channges, default methods are public
+ // Lambda methods are private synthetic.
translatedSym.flags_field = SYNTHETIC |
- ((inInterface && thisReferenced)? PUBLIC : PRIVATE) |
+ PRIVATE |
(thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
//compute synthetic params
@@ -1890,7 +1889,7 @@
super(tree);
this.isSuper = tree.hasKind(ReferenceKind.SUPER);
this.bridgeSym = needsBridge()
- ? makeSyntheticMethod(isSuper ? 0 : STATIC,
+ ? makePrivateSyntheticMethod(isSuper ? 0 : STATIC,
lambdaName().append(names.fromString("$bridge")), null,
owner.enclClass())
: null;
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Pool.java Thu Oct 10 20:57:27 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Pool.java Thu Oct 10 23:26:56 2013 -0700
@@ -296,7 +296,10 @@
interfaceOwner = true;
staticOk = true;
case ClassFile.REF_invokeVirtual:
+ expectedKind = Kinds.MTH;
+ break;
case ClassFile.REF_invokeSpecial:
+ interfaceOwner = true;
expectedKind = Kinds.MTH;
break;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8012557/A.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+interface A {
+ default String u() { return "A"; }
+ default String name() {
+ SAM s = ()->u()+"A";
+ return s.m();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8012557/B.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+interface B {
+ default String u() { return "B"; }
+ default String name() {
+ SAM s = ()->u()+"B";
+ return s.m();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8012557/C.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+interface C {
+ default String u() { return "C"; }
+ default String name() {
+ SAM s = ()->u()+"C";
+ return s.m();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8012557/PrivateLambdas.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8012557
+ * @summary Check that 8012557 is fixed, that interface lambda
+ * methods are private
+ * @author Robert Field
+ * @compile SAM.java
+ * @compile A.java
+ * @compile B.java
+ * @compile C.java
+ * @run main PrivateLambdas
+ *
+ * Unless the lambda methods are private, this will fail with:
+ * AbstractMethodError:
+ * Conflicting default methods: A.lambda$0 B.lambda$0 C.lambda$0
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+interface X extends A, B, C {
+ default String u() { return " "; }
+ default String name() {
+ return A.super.name() + B.super.name() + C.super.name();
+ }
+}
+
+public class PrivateLambdas implements X {
+ public static void main(String[] args) throws Exception {
+
+ // Check that all the lambda methods are private instance synthetic
+ for (Class<?> k : new Class<?>[] { A.class, B.class, C.class }) {
+ Method[] methods = k.getDeclaredMethods();
+ int lambdaCount = 0;
+ for(Method m : methods) {
+ if (m.getName().startsWith("lambda$")) {
+ ++lambdaCount;
+ int mod = m.getModifiers();
+ if ((mod & Modifier.PRIVATE) == 0) {
+ throw new Exception("Expected " + m + " to be private");
+ }
+ if (!m.isSynthetic()) {
+ throw new Exception("Expected " + m + " to be synthetic");
+ }
+ if ((mod & Modifier.STATIC) != 0) {
+ throw new Exception("Expected " + m + " to be instance method");
+ }
+ }
+ }
+ if (lambdaCount == 0) {
+ throw new Exception("Expected at least one lambda method");
+ }
+ }
+
+ /*
+ * Unless the lambda methods are private, this will fail with:
+ * AbstractMethodError:
+ * Conflicting default methods: A.lambda$0 B.lambda$0 C.lambda$0
+ */
+ X x = new PrivateLambdas();
+ if (!x.name().equals(" A B C")) {
+ throw new Exception("Expected ' A B C' got: " + x.name());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8012557/SAM.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+interface SAM {
+ String m();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8016320/IllegalBridgeModifier.java Thu Oct 10 23:26:56 2013 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8016320
+ * @summary Check that 8016320 is fixed,
+ * that bridges have valid modifier bits
+ * @author Robert Field
+ * @run main IllegalBridgeModifier
+ */
+
+interface SAM {
+ int m();
+}
+
+interface SuperI {
+ public default int foo() { return 1234; }
+}
+
+interface I extends SuperI {
+}
+
+interface T extends I {
+ public default SAM boo() { return I.super::foo; }
+}
+
+public class IllegalBridgeModifier {
+ public static void main(String argv[])throws Exception {
+ T t = new T(){};
+ if (t.boo().m() != 1234) {
+ throw new Exception("Failed test");
+ }
+ }
+}