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;
+ }
+ }
}