8012557: Implement lambda methods on interfaces as private
authorrfield
Thu, 10 Oct 2013 23:26:56 -0700
changeset 21014 57913337d634
parent 21013 ced599ab1495
child 21015 f3bec12a63e7
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
langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
langtools/src/share/classes/com/sun/tools/javac/jvm/Pool.java
langtools/test/tools/javac/lambda/8012557/A.java
langtools/test/tools/javac/lambda/8012557/B.java
langtools/test/tools/javac/lambda/8012557/C.java
langtools/test/tools/javac/lambda/8012557/PrivateLambdas.java
langtools/test/tools/javac/lambda/8012557/SAM.java
langtools/test/tools/javac/lambda/8016320/IllegalBridgeModifier.java
--- 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");
+        }
+    }
+}