src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java

Print this page

        

@@ -22,16 +22,19 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package java.lang.invoke;
 
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import sun.invoke.util.Wrapper;
+import static sun.invoke.util.Wrapper.*;
 
-import static sun.invoke.util.Wrapper.forPrimitiveType;
-import static sun.invoke.util.Wrapper.forWrapperType;
-import static sun.invoke.util.Wrapper.isWrapperType;
-
 /**
  * Abstract implementation of a lambda metafactory which provides parameter unrolling and input validation.
  *
  * @see LambdaMetafactory
  */

@@ -62,56 +65,38 @@
     final Class<?> implDefiningClass;         // Type defining the implementation "class CC"
     final MethodType implMethodType;          // Type of the implementation method "(int)String"
     final MethodType instantiatedMethodType;  // Instantiated erased functional interface method type "(Integer)Object"
     final boolean isSerializable;             // Should the returned instance be serializable
     final Class<?>[] markerInterfaces;        // Additional marker interfaces to be implemented
-    final MethodType[] additionalBridges;     // Signatures of additional methods to bridge
 
 
     /**
      * Meta-factory constructor.
      *
-     * @param caller Stacked automatically by VM; represents a lookup context
-     *               with the accessibility privileges of the caller.
-     * @param invokedType Stacked automatically by VM; the signature of the
-     *                    invoked method, which includes the expected static
-     *                    type of the returned lambda object, and the static
-     *                    types of the captured arguments for the lambda.  In
-     *                    the event that the implementation method is an
-     *                    instance method, the first argument in the invocation
-     *                    signature will correspond to the receiver.
-     * @param samMethod The primary method in the functional interface to which
-     *                  the lambda or method reference is being converted,
-     *                  represented as a method handle.
-     * @param implMethod The implementation method which should be called
-     *                   (with suitable adaptation of argument types, return
-     *                   types, and adjustment for captured arguments) when
-     *                   methods of the resulting functional interface instance
-     *                   are invoked.
-     * @param instantiatedMethodType The signature of the primary functional
-     *                               interface method after type variables are
-     *                               substituted with their instantiation from
-     *                               the capture site
-     * @param isSerializable Should the lambda be made serializable?  If set,
-     *                       either the target type or one of the additional SAM
-     *                       types must extend {@code Serializable}.
-     * @param markerInterfaces Additional interfaces which the lambda object
-     *                       should implement.
-     * @param additionalBridges Method types for additional signatures to be
-     *                          bridged to the implementation method
+     * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
+     *               of the caller.
+     * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
+     *                    expected static type of the returned lambda object, and the static types of the captured
+     *                    arguments for the lambda.  In the event that the implementation method is an instance method,
+     *                    the first argument in the invocation signature will correspond to the receiver.
+     * @param samMethod The primary method in the functional interface to which the lambda or method reference is
+     *                  being converted, represented as a method handle.
+     * @param implMethod The implementation method which should be called (with suitable adaptation of argument
+     *                   types, return types, and adjustment for captured arguments) when methods of the resulting
+     *                   functional interface instance are invoked.
+     * @param instantiatedMethodType The signature of the primary functional interface method after type variables
+     *                               are substituted with their instantiation from the capture site
      * @throws ReflectiveOperationException
-     * @throws LambdaConversionException If any of the meta-factory protocol
-     * invariants are violated
+     * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
      */
     AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
                                        MethodType invokedType,
                                        MethodHandle samMethod,
                                        MethodHandle implMethod,
                                        MethodType instantiatedMethodType,
-                                       boolean isSerializable,
-                                       Class<?>[] markerInterfaces,
-                                       MethodType[] additionalBridges)
+                                       int flags,
+                                       Class<?>[] markerInterfaces)
             throws ReflectiveOperationException, LambdaConversionException {
         this.targetClass = caller.lookupClass();
         this.invokedType = invokedType;
 
         this.samBase = invokedType.returnType();

@@ -131,27 +116,37 @@
                 implKind == MethodHandleInfo.REF_invokeVirtual ||
                 implKind == MethodHandleInfo.REF_invokeSpecial ||
                 implKind == MethodHandleInfo.REF_invokeInterface;
         this.implDefiningClass = implInfo.getDeclaringClass();
         this.implMethodType = implInfo.getMethodType();
+
         this.instantiatedMethodType = instantiatedMethodType;
-        this.isSerializable = isSerializable;
-        this.markerInterfaces = markerInterfaces;
-        this.additionalBridges = additionalBridges;
 
         if (!samClass.isInterface()) {
             throw new LambdaConversionException(String.format(
-                    "Functional interface %s is not an interface", samClass.getName()));
+                    "Functional interface %s is not an interface",
+                    samClass.getName()));
         }
 
+        boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(samBase);
         for (Class<?> c : markerInterfaces) {
             if (!c.isInterface()) {
                 throw new LambdaConversionException(String.format(
-                        "Marker interface %s is not an interface", c.getName()));
+                        "Marker interface %s is not an interface",
+                        c.getName()));
             }
+            foundSerializableSupertype |= Serializable.class.isAssignableFrom(c);
         }
+        this.isSerializable = ((flags & LambdaMetafactory.FLAG_SERIALIZABLE) != 0)
+                              || foundSerializableSupertype;
+
+        if (isSerializable && !foundSerializableSupertype) {
+            markerInterfaces = Arrays.copyOf(markerInterfaces, markerInterfaces.length + 1);
+            markerInterfaces[markerInterfaces.length-1] = Serializable.class;
     }
+        this.markerInterfaces = markerInterfaces;
+    }
 
     /**
      * Build the CallSite.
      *
      * @return a CallSite, which, when invoked, will return an instance of the

@@ -268,13 +263,13 @@
                                   actualReturnType, expectedType));
         }
      }
 
     /**
-     * Check type adaptability for parameter types.
-     * @param fromType Type to convert from
-     * @param toType Type to convert to
+     * Check type adaptability
+     * @param fromType
+     * @param toType
      * @param strict If true, do strict checks, else allow that fromType may be parameterized
      * @return True if 'fromType' can be passed to an argument of 'toType'
      */
     private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
         if (fromType.equals(toType)) {

@@ -302,18 +297,19 @@
                     // must be convertible to primitive
                     return !strict;
                 }
             } else {
                 // both are reference types: fromType should be a superclass of toType.
-                return !strict || toType.isAssignableFrom(fromType);
+                return strict? toType.isAssignableFrom(fromType) : true;
             }
         }
     }
 
     /**
-     * Check type adaptability for return types -- special handling of void type)
-     * and parameterized fromType
+     * Check type adaptability for return types -- special handling of void type) and parameterized fromType
+     * @param fromType
+     * @param toType
      * @return True if 'fromType' can be converted to 'toType'
      */
     private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
         return toType.equals(void.class)
                || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);

@@ -340,6 +336,91 @@
             }
         });
     }
     ***********************/
 
+    /**
+     * Find the functional interface method and corresponding abstract methods
+     * which should be bridged. The functional interface method and those to be
+     * bridged will have the same name and number of parameters. Check for
+     * matching default methods (non-abstract), the VM will create bridges for
+     * default methods; We don't have enough readily available type information
+     * to distinguish between where the functional interface method should be
+     * bridged and where the default method should be bridged; This situation is
+     * flagged.
+     */
+    class MethodAnalyzer {
+        private final Method[] methods = samBase.getMethods();
+
+        private Method samMethod = null;
+        private final List<Method> methodsToBridge = new ArrayList<>(methods.length);
+        private boolean conflictFoundBetweenDefaultAndBridge = false;
+
+        MethodAnalyzer() {
+            String samMethodName = samInfo.getName();
+            Class<?>[] samParamTypes = samMethodType.parameterArray();
+            int samParamLength = samParamTypes.length;
+            Class<?> samReturnType = samMethodType.returnType();
+            Class<?> objectClass = Object.class;
+            List<Method> defaultMethods = new ArrayList<>(methods.length);
+
+            for (Method m : methods) {
+                if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) {
+                    Class<?>[] mParamTypes = m.getParameterTypes();
+                    if (mParamTypes.length == samParamLength) {
+                        // Method matches name and parameter length -- and is not Object
+                        if (Modifier.isAbstract(m.getModifiers())) {
+                            // Method is abstract
+                            if (m.getReturnType().equals(samReturnType)
+                                    && Arrays.equals(mParamTypes, samParamTypes)) {
+                                // Exact match, this is the SAM method signature
+                                samMethod = m;
+                            } else if (!hasMatchingBridgeSignature(m)) {
+                                // Record bridges, exclude methods with duplicate signatures
+                                methodsToBridge.add(m);
+                            }
+                        } else {
+                            // Record default methods for conflict testing
+                            defaultMethods.add(m);
+                        }
+                    }
+                }
+            }
+            for (Method dm : defaultMethods) {
+                if (hasMatchingBridgeSignature(dm)) {
+                    conflictFoundBetweenDefaultAndBridge = true;
+                    break;
+                }
+            }
+        }
+
+        Method getSamMethod() {
+            return samMethod;
+        }
+
+        List<Method> getMethodsToBridge() {
+            return methodsToBridge;
+        }
+
+        boolean conflictFoundBetweenDefaultAndBridge() {
+            return conflictFoundBetweenDefaultAndBridge;
+        }
+
+        /**
+         * Search the list of previously found bridge methods to determine if there is a method with the same signature
+         * (return and parameter types) as the specified method.
+         *
+         * @param m The method to match
+         * @return True if the method was found, False otherwise
+         */
+        private boolean hasMatchingBridgeSignature(Method m) {
+            Class<?>[] ptypes = m.getParameterTypes();
+            Class<?> rtype = m.getReturnType();
+            for (Method md : methodsToBridge) {
+                if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
+                    return true;
+                }
+            }
+                    return false;
+                }
+            }
 }