changeset 2009:f8c62319e8b8

Add experimental support for compile-time interface bridging Compile-time interface bridges can be enabled with the flag '-XDallowInterfaceBridges'.
author mcimadamore
date Tue, 16 Apr 2013 16:47:03 +0100
parents 4c83889e32c8
children b0716aee0da8
files src/share/classes/com/sun/tools/javac/comp/Check.java src/share/classes/com/sun/tools/javac/comp/TransTypes.java test/tools/javac/defaultMethods/TestInterfaceBridges.java test/tools/javac/defaultMethods/TestNoBridgeOnDefaults.java
diffstat 4 files changed, 281 insertions(+), 121 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Apr 16 15:42:29 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Tue Apr 16 16:47:03 2013 +0100
@@ -2473,7 +2473,7 @@
                 ListBuffer<Symbol> abstracts = ListBuffer.lb();
                 ListBuffer<Symbol> defaults = ListBuffer.lb();
                 for (MethodSymbol provSym : prov) {
-                    if ((provSym.flags() & DEFAULT) != 0) {
+                    if ((provSym.flags() & (DEFAULT | SYNTHETIC)) == DEFAULT) {
                         defaults = defaults.append(provSym);
                     } else if ((provSym.flags() & ABSTRACT) != 0) {
                         abstracts = abstracts.append(provSym);
--- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Tue Apr 16 15:42:29 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Tue Apr 16 16:47:03 2013 +0100
@@ -67,6 +67,7 @@
     private TreeMaker make;
     private Enter enter;
     private boolean allowEnums;
+    private boolean allowInterfaceBridges;
     private Types types;
     private final Resolve resolve;
 
@@ -87,6 +88,9 @@
         Source source = Source.instance(context);
         allowEnums = source.allowEnums();
         addBridges = source.addBridges();
+        Options options = Options.instance(context);
+        allowInterfaceBridges = source.allowDefaultMethods() &&
+                options.isSet("allowInterfaceBridges");
         types = Types.instance(context);
         make = TreeMaker.instance(context);
         resolve = Resolve.instance(context);
@@ -238,14 +242,15 @@
                    MethodSymbol impl,
                    ClassSymbol origin,
                    boolean hypothetical,
-                   ListBuffer<JCTree> bridges) {
+                   ListBuffer<JCTree> bridges,
+                   Bridger bridger) {
         make.at(pos);
         Type origType = types.memberType(origin.type, meth);
         Type origErasure = erasure(origType);
 
         // Create a bridge method symbol and a bridge definition without a body.
         Type bridgeType = meth.erasure(types);
-        long flags = impl.flags() & AccessFlags | SYNTHETIC | BRIDGE;
+        long flags = impl.flags() & AccessFlags | SYNTHETIC | BRIDGE | bridger.bridgeFlags();
         if (hypothetical) flags |= HYPOTHETICAL;
         MethodSymbol bridge = new MethodSymbol(flags,
                                                meth.name,
@@ -306,7 +311,8 @@
     void addBridgeIfNeeded(DiagnosticPosition pos,
                            Symbol sym,
                            ClassSymbol origin,
-                           ListBuffer<JCTree> bridges) {
+                           ListBuffer<JCTree> bridges,
+                           Bridger bridger) {
         if (sym.kind == MTH &&
             sym.name != names.init &&
             (sym.flags() & (PRIVATE | STATIC)) == 0 &&
@@ -314,14 +320,14 @@
             sym.isMemberOf(origin, types))
         {
             MethodSymbol meth = (MethodSymbol)sym;
-            MethodSymbol bridge = meth.binaryImplementation(origin, types);
-            MethodSymbol impl = meth.implementation(origin, types, true, overrideBridgeFilter);
+            MethodSymbol bridge = bridger.binaryImplementation(meth, origin);
+            MethodSymbol impl = bridger.implementation(meth, origin);
             if (bridge == null ||
                 bridge == meth ||
                 (impl != null && !bridge.owner.isSubClass(impl.owner, types))) {
                 // No bridge was added yet.
                 if (impl != null && isBridgeNeeded(meth, impl, origin.type)) {
-                    addBridge(pos, meth, impl, origin, bridge==impl, bridges);
+                    addBridge(pos, meth, impl, origin, bridge==impl, bridges, bridger);
                 } else if (impl == meth
                            && impl.owner != origin
                            && (impl.flags() & FINAL) == 0
@@ -329,36 +335,34 @@
                            && (origin.flags() & PUBLIC) > (impl.owner.flags() & PUBLIC)) {
                     // this is to work around a horrible but permanent
                     // reflection design error.
-                    addBridge(pos, meth, impl, origin, false, bridges);
+                    addBridge(pos, meth, impl, origin, false, bridges, bridger);
                 }
-            } else if ((bridge.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) == SYNTHETIC) {
-                MethodSymbol other = overridden.get(bridge);
-                if (other != null && other != meth) {
-                    if (impl == null || !impl.overrides(other, origin, types, true)) {
-                        // Bridge for other symbol pair was added
+            } else if (!allowInterfaceBridges) {
+                //close gaps left open during type-checking
+                if ((bridge.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) == SYNTHETIC) {
+                    MethodSymbol other = overridden.get(bridge);
+                    if (other != null && other != meth) {
+                        if (impl == null || !impl.overrides(other, origin, types, true)) {
+                            // Bridge for other symbol pair was added
+                            log.error(pos, "name.clash.same.erasure.no.override",
+                                      other, other.location(origin.type, types),
+                                      meth,  meth.location(origin.type, types));
+                        }
+                    }
+                } else if (!bridge.overrides(meth, origin, types, true)) {
+                    // Accidental binary override without source override.
+                    if (bridge.owner == origin ||
+                        types.asSuper(bridge.owner.type, meth.owner) == null)
+                        // Don't diagnose the problem if it would already
+                        // have been reported in the superclass
                         log.error(pos, "name.clash.same.erasure.no.override",
-                                  other, other.location(origin.type, types),
+                                  bridge, bridge.location(origin.type, types),
                                   meth,  meth.location(origin.type, types));
-                    }
                 }
-            } else if (!bridge.overrides(meth, origin, types, true)) {
-                // Accidental binary override without source override.
-                if (bridge.owner == origin ||
-                    types.asSuper(bridge.owner.type, meth.owner) == null)
-                    // Don't diagnose the problem if it would already
-                    // have been reported in the superclass
-                    log.error(pos, "name.clash.same.erasure.no.override",
-                              bridge, bridge.location(origin.type, types),
-                              meth,  meth.location(origin.type, types));
             }
         }
     }
     // where
-        Filter<Symbol> overrideBridgeFilter = new Filter<Symbol>() {
-            public boolean accepts(Symbol s) {
-                return (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC;
-            }
-        };
         /**
          * @param method The symbol for which a bridge might have to be added
          * @param impl The implementation of method
@@ -412,11 +416,12 @@
     void addBridges(DiagnosticPosition pos,
                     TypeSymbol i,
                     ClassSymbol origin,
-                    ListBuffer<JCTree> bridges) {
+                    ListBuffer<JCTree> bridges,
+                    Bridger bridger) {
         for (Scope.Entry e = i.members().elems; e != null; e = e.sibling)
-            addBridgeIfNeeded(pos, e.sym, origin, bridges);
+            addBridgeIfNeeded(pos, e.sym, origin, bridges, bridger);
         for (List<Type> l = types.interfaces(i.type); l.nonEmpty(); l = l.tail)
-            addBridges(pos, l.head.tsym, origin, bridges);
+            addBridges(pos, l.head.tsym, origin, bridges, bridger);
     }
 
     /** Add all necessary bridges to some class appending them to list buffer.
@@ -424,16 +429,16 @@
      *  @param origin  The class in which the bridges go.
      *  @param bridges The list buffer to which the bridges are added.
      */
-    void addBridges(DiagnosticPosition pos, ClassSymbol origin, ListBuffer<JCTree> bridges) {
+    void addBridges(DiagnosticPosition pos, ClassSymbol origin, ListBuffer<JCTree> bridges, Bridger bridger) {
         Type st = types.supertype(origin.type);
         while (st.hasTag(CLASS)) {
 //          if (isSpecialization(st))
-            addBridges(pos, st.tsym, origin, bridges);
+            addBridges(pos, st.tsym, origin, bridges, bridger);
             st = types.supertype(st);
         }
         for (List<Type> l = types.interfaces(origin.type); l.nonEmpty(); l = l.tail)
 //          if (isSpecialization(l.head))
-            addBridges(pos, l.head.tsym, origin, bridges);
+            addBridges(pos, l.head.tsym, origin, bridges, bridger);
     }
 
 /* ************************************************************************
@@ -942,8 +947,11 @@
                     ListBuffer<JCTree> bridges = new ListBuffer<JCTree>();
                     if (false) //see CR: 6996415
                         bridges.appendList(addOverrideBridgesIfNeeded(tree, c));
-                    if ((tree.sym.flags() & INTERFACE) == 0)
-                        addBridges(tree.pos(), tree.sym, bridges);
+                    boolean bridgeInterface = (tree.sym.flags() & INTERFACE) != 0;
+                    if (!bridgeInterface || allowInterfaceBridges) {
+                        addBridges(tree.pos(), tree.sym, bridges,
+                            bridgeInterface ? interfaceBridger : classBridger);
+                    }
                     tree.defs = bridges.toList().prependList(tree.defs);
                 }
                 tree.type = erasure(tree.type);
@@ -955,6 +963,156 @@
             env = oldEnv;
         }
     }
+    
+    /**
+     * This is an helper object is used by the bridge generation logic routine.
+     */
+    interface Bridger {
+        /**
+         * Is there another method in the class hierarchy induced by {@code origin}
+         * that is binary equivalent with a given method {@code m)?
+         */
+        MethodSymbol binaryImplementation(MethodSymbol m, ClassSymbol origin);
+        /**
+         * Is a given method {@code m) overridden in the class hierarchy induced
+         * by {@code origin} ?
+         */
+        MethodSymbol implementation(MethodSymbol m, ClassSymbol origin);
+        /**
+         * What are the flags that should be appended to the bridge method?
+         */
+        public long bridgeFlags();
+    }
+    
+    /**
+     * This is the standard bridge helper class for class hierarchies.
+     */
+    Bridger classBridger = new Bridger() {
+        @Override
+        public MethodSymbol binaryImplementation(MethodSymbol m, ClassSymbol origin) {
+            return m.binaryImplementation(origin, types);
+        }
+
+        @Override
+        public MethodSymbol implementation(MethodSymbol m, ClassSymbol origin) {
+            return types.implementation(m, origin, true, overrideBridgeFilter);
+        }
+        //where
+            private Filter<Symbol> overrideBridgeFilter = new Filter<Symbol>() {
+                public boolean accepts(Symbol s) {
+                    return (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC;
+                }
+            };
+        
+        @Override
+        public long bridgeFlags() {
+            return 0;
+        }
+    };
+        
+    /**
+     * This is an alternate bridge helper class that is used in order to bridge
+     * interface hierarchies.
+     */
+    Bridger interfaceBridger = new Bridger() {
+        @Override
+        public MethodSymbol binaryImplementation(MethodSymbol m, ClassSymbol origin) {
+            ClassSymbol fakeOrigin = makeSyntheticClass(origin);
+            return base(classBridger.binaryImplementation(m, fakeOrigin));
+        }
+
+        @Override
+        public MethodSymbol implementation(MethodSymbol m, ClassSymbol origin) {
+            ClassSymbol fakeOrigin = makeSyntheticClass(origin);
+            return base(classBridger.implementation(m, fakeOrigin));
+        }
+        
+        public long bridgeFlags() {
+            return DEFAULT | ABSTRACT;
+        }
+        
+        /**
+         * Get base symbol for a given method symbol (if other than null)
+         */
+        private MethodSymbol base(MethodSymbol s) {
+            return s == null ? null : (MethodSymbol)s.baseSymbol();
+        }
+        
+        /**
+         * Creates a synthetic abstract class from interface if needed (see below).
+         * This step is required to allow bridge calculation logic to work uniformly
+         * on interfaces.
+         */
+        private ClassSymbol makeSyntheticClass(ClassSymbol origin) {
+            Entry _e = syntheticClasses.get(origin);
+            Scope.CompoundScope cs = types.membersClosure(origin.type, false);
+            if (_e == null || !_e.isValid(cs.getMark())) {
+                ClassSymbol csym = makeSyntheticClassInternal(origin);
+                syntheticClasses.put(origin, new Entry(csym, cs.getMark()));
+                return csym;
+            } else {
+                return _e.csym;
+            }
+        }
+        
+        /**
+         * Creates a synthetic abstract class from a given interface. The synthetic
+         * abstract class will contain all the method symbols defined in the original
+         * interface. Those symbols will be copied over as new symbols which will
+         * point back to the original symbols using {@code Symbol.baseSymbol}.
+         */
+        private ClassSymbol makeSyntheticClassInternal(ClassSymbol origin) {
+            long cflags = (origin.flags() & ~INTERFACE) | STATIC | ABSTRACT | PUBLIC;
+            ClassSymbol csym = new ClassSymbol(cflags, origin.name, origin.owner);
+            csym.completer = null;
+            csym.members_field = new Scope(csym);
+            for (Symbol s : origin.members().getElements(new Filter<Symbol>() {
+                public boolean accepts(Symbol t) {
+                    //all interface methods can be bridged
+                    return t.kind == MTH;
+                }
+            })) {
+                final MethodSymbol msym = (MethodSymbol)s;
+                long mflags = msym.flags() & ~DEFAULT;
+                csym.members_field.enter(new MethodSymbol(mflags, msym.name, msym.type, csym) {
+                    @Override
+                    public Symbol baseSymbol() {
+                        return msym;
+                    }
+                });
+            }
+            Type.ClassType ctype = new Type.ClassType(origin.type.getEnclosingType(), origin.type.getTypeArguments(), csym);
+            ctype.supertype_field = syms.objectType;
+            ctype.interfaces_field = types.interfaces(origin.type);
+            csym.type = ctype;
+            return csym;
+        }
+        
+        /**
+         * Entry used to cache synthetic classes generated by this bridger object.
+         * An entry is characterized by a mark which is used to tell as to whether
+         * the synthetic class needs to be recalculated (this can happen if
+         * the original interface scope has changed between subsequent
+         * bridge calculations).
+         */
+        class Entry {
+            ClassSymbol csym;
+            int mark;
+
+            Entry(ClassSymbol csym, int mark) {
+                this.csym = csym;
+                this.mark = mark;
+            }
+            
+            boolean isValid(int mark) {
+                return mark == this.mark;
+            }
+        }
+        
+        /** Synthetic abstract class cache */
+        private WeakHashMap<ClassSymbol, Entry> syntheticClasses =
+                new WeakHashMap<ClassSymbol, Entry>();
+    };
 
     /** Translate a toplevel class definition.
      *  @param cdef    The definition to be translated.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defaultMethods/TestInterfaceBridges.java	Tue Apr 16 16:47:03 2013 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011, 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
+ * @summary Add experimental support for interface bridging
+ * @compile -XDallowInterfaceBridges TestInterfaceBridges.java
+ * @run main TestInterfaceBridges
+ */
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.classfile.Method;
+
+import java.io.*;
+
+public class TestInterfaceBridges {
+
+    interface A1 { Object m(); }
+    interface A2 { Object m(); }
+    interface B extends A1, A2 { B m(); }
+
+    static final String TEST_NAME = B.class.getName() + ".class" ;
+    static final String TEST_METHOD_NAME = "m";
+    static final String[] SIGS = new String[] { "()LTestInterfaceBridges$B;", "()Ljava/lang/Object;" };
+
+    public static void main(String... args) throws Exception {
+        new TestInterfaceBridges().run();
+    }
+
+    public void run() throws Exception {
+        String workDir = System.getProperty("test.classes");
+        checkInterfaceBridges(new File(workDir, TEST_NAME));
+    }
+
+    void checkInterfaceBridges(File f) {
+        System.err.println("check: " + f);
+        try {
+            ClassFile cf = ClassFile.read(f);
+            boolean[] sigFound = new boolean[] { false, false };
+            for (Method m : cf.methods) {
+                String mname = m.getName(cf.constant_pool);
+                if (mname.equals(TEST_METHOD_NAME)) {
+                    System.err.println(m.descriptor.getValue(cf.constant_pool));
+                    for (int i = 0 ; i < SIGS.length ; i++) {
+                        if (SIGS[i].equals(m.descriptor.getValue(cf.constant_pool))) {
+                            if (sigFound[i]) {
+                                throw new AssertionError("Duplicate method found: " + SIGS[i]);
+                            } else {
+                                sigFound[i] = true;
+                            }
+                        }
+                    }
+                }
+            }
+            for (int i = 0 ; i < sigFound.length ; i++) {
+                if (!sigFound[i]) {
+                    throw new AssertionError("Method not found: " + SIGS[i]);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new Error("error reading " + f +": " + e);
+        }
+    }
+}
--- a/test/tools/javac/defaultMethods/TestNoBridgeOnDefaults.java	Tue Apr 16 15:42:29 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2011, 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
- * @summary  check that javac does not generate bridge methods for defaults
- */
-
-import com.sun.tools.classfile.ClassFile;
-import com.sun.tools.classfile.ConstantPool.*;
-import com.sun.tools.classfile.Method;
-
-import java.io.*;
-
-public class TestNoBridgeOnDefaults {
-
-    interface A<X> {
-        default <Y> A<X> m(X x, Y y) { return Impl.<X,Y>m1(this, x, y); }
-    }
-
-    static abstract class B<X> implements A<X> { }
-
-    interface C<X> extends A<X> {
-        default <Y> C<X> m(X x, Y y) { return Impl.<X,Y>m2(this, x, y); }
-    }
-
-    static abstract class D<X> extends B<X> implements C<X> { }
-
-    static class Impl {
-       static <X, Y> A<X> m1(A<X> rec, X x, Y y) { return null; }
-       static <X, Y> C<X> m2(C<X> rec, X x, Y y) { return null; }
-    }
-
-    static final String[] SUBTEST_NAMES = { B.class.getName() + ".class", D.class.getName() + ".class" };
-    static final String TEST_METHOD_NAME = "m";
-
-    public static void main(String... args) throws Exception {
-        new TestNoBridgeOnDefaults().run();
-    }
-
-    public void run() throws Exception {
-        String workDir = System.getProperty("test.classes");
-        for (int i = 0 ; i < SUBTEST_NAMES.length ; i ++) {
-            File compiledTest = new File(workDir, SUBTEST_NAMES[i]);
-            checkNoBridgeOnDefaults(compiledTest);
-        }
-    }
-
-    void checkNoBridgeOnDefaults(File f) {
-        System.err.println("check: " + f);
-        try {
-            ClassFile cf = ClassFile.read(f);
-            for (Method m : cf.methods) {
-                String mname = m.getName(cf.constant_pool);
-                if (mname.equals(TEST_METHOD_NAME)) {
-                    throw new Error("unexpected bridge method found " + m);
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new Error("error reading " + f +": " + e);
-        }
-    }
-}