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

Print this page

        

*** 22,37 **** * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang.invoke; import 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 */ --- 22,40 ---- * 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.*; /** * Abstract implementation of a lambda metafactory which provides parameter unrolling and input validation. * * @see LambdaMetafactory */
*** 62,117 **** 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 * @throws ReflectiveOperationException ! * @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) throws ReflectiveOperationException, LambdaConversionException { this.targetClass = caller.lookupClass(); this.invokedType = invokedType; this.samBase = invokedType.returnType(); --- 65,102 ---- 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 /** * 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 * @throws ReflectiveOperationException ! * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated */ AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, MethodHandle samMethod, MethodHandle implMethod, MethodType instantiatedMethodType, ! int flags, ! Class<?>[] markerInterfaces) throws ReflectiveOperationException, LambdaConversionException { this.targetClass = caller.lookupClass(); this.invokedType = invokedType; this.samBase = invokedType.returnType();
*** 131,157 **** 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())); } for (Class<?> c : markerInterfaces) { if (!c.isInterface()) { throw new LambdaConversionException(String.format( ! "Marker interface %s is not an interface", c.getName())); } } } /** * Build the CallSite. * * @return a CallSite, which, when invoked, will return an instance of the --- 116,152 ---- implKind == MethodHandleInfo.REF_invokeVirtual || implKind == MethodHandleInfo.REF_invokeSpecial || implKind == MethodHandleInfo.REF_invokeInterface; this.implDefiningClass = implInfo.getDeclaringClass(); this.implMethodType = implInfo.getMethodType(); + this.instantiatedMethodType = instantiatedMethodType; if (!samClass.isInterface()) { throw new LambdaConversionException(String.format( ! "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())); } + 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,280 **** actualReturnType, expectedType)); } } /** ! * Check type adaptability for parameter types. ! * @param fromType Type to convert from ! * @param toType Type to convert to * @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)) { --- 263,275 ---- actualReturnType, expectedType)); } } /** ! * 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,319 **** // must be convertible to primitive return !strict; } } else { // both are reference types: fromType should be a superclass of toType. ! return !strict || toType.isAssignableFrom(fromType); } } } /** ! * Check type adaptability for return types -- special handling of void type) ! * and parameterized fromType * @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); --- 297,315 ---- // must be convertible to primitive return !strict; } } else { // both are reference types: fromType should be a superclass of toType. ! return strict? toType.isAssignableFrom(fromType) : true; } } } /** ! * 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,345 **** --- 336,426 ---- } }); } ***********************/ + /** + * 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; + } + } }