8016281: The SAM method should be passed to the metafactory as a MethodType not a MethodHandle
8020010: Move lambda bridge creation from metafactory and VM to compiler
Summary: JDK/metafactory component of the bridge fix and and MethodType vs. MethodHandle changes.
Reviewed-by: twisti, briangoetz, forax
Contributed-by: robert.field@oracle.com
--- a/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Thu Jul 11 10:13:49 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Thu Jul 11 14:02:20 2013 +0100
@@ -24,24 +24,23 @@
*/
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.
+ * Abstract implementation of a lambda metafactory which provides parameter
+ * unrolling and input validation.
*
* @see LambdaMetafactory
*/
/* package */ abstract class AbstractValidatingLambdaMetafactory {
/*
- * For context, the comments for the following fields are marked in quotes with their values, given this program:
+ * For context, the comments for the following fields are marked in quotes
+ * with their values, given this program:
* interface II<T> { Object foo(T x); }
* interface JJ<R extends Number> extends II<R> { }
* class CC { String impl(int i) { return "impl:"+i; }}
@@ -54,9 +53,7 @@
final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
final MethodType invokedType; // The type of the invoked method "(CC)II"
final Class<?> samBase; // The type of the returned instance "interface JJ"
- final MethodHandle samMethod; // Raw method handle for the functional interface method
- final MethodHandleInfo samInfo; // Info about the SAM method handle "MethodHandleInfo[9 II.foo(Object)Object]"
- final Class<?> samClass; // Interface containing the SAM method "interface II"
+ final String samMethodName; // Name of the SAM method "foo"
final MethodType samMethodType; // Type of the SAM method "(Object)Object"
final MethodHandle implMethod; // Raw method handle for the implementation method
final MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]"
@@ -67,44 +64,64 @@
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 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 samMethodName Name of the method in the functional interface to
+ * which the lambda or method reference is being
+ * converted, represented as a String.
+ * @param samMethodType Type of the method in the functional interface to
+ * which the lambda or method reference is being
+ * converted, represented as a MethodType.
+ * @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
+ * @throws LambdaConversionException If any of the meta-factory protocol
+ * invariants are violated
*/
AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
MethodType invokedType,
- MethodHandle samMethod,
+ String samMethodName,
+ MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType,
- int flags,
- Class<?>[] markerInterfaces)
+ boolean isSerializable,
+ Class<?>[] markerInterfaces,
+ MethodType[] additionalBridges)
throws ReflectiveOperationException, LambdaConversionException {
this.targetClass = caller.lookupClass();
this.invokedType = invokedType;
this.samBase = invokedType.returnType();
- this.samMethod = samMethod;
- this.samInfo = new MethodHandleInfo(samMethod);
- this.samClass = samInfo.getDeclaringClass();
- this.samMethodType = samInfo.getMethodType();
+ this.samMethodName = samMethodName;
+ this.samMethodType = samMethodType;
this.implMethod = implMethod;
this.implInfo = new MethodHandleInfo(implMethod);
@@ -118,32 +135,24 @@
implKind == MethodHandleInfo.REF_invokeInterface;
this.implDefiningClass = implInfo.getDeclaringClass();
this.implMethodType = implInfo.getMethodType();
+ this.instantiatedMethodType = instantiatedMethodType;
+ this.isSerializable = isSerializable;
+ this.markerInterfaces = markerInterfaces;
+ this.additionalBridges = additionalBridges;
- this.instantiatedMethodType = instantiatedMethodType;
-
- if (!samClass.isInterface()) {
+ if (!samBase.isInterface()) {
throw new LambdaConversionException(String.format(
"Functional interface %s is not an interface",
- samClass.getName()));
+ samBase.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;
}
/**
@@ -153,20 +162,14 @@
* functional interface
* @throws ReflectiveOperationException
*/
- abstract CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException;
+ abstract CallSite buildCallSite()
+ throws ReflectiveOperationException, LambdaConversionException;
/**
* Check the meta-factory arguments for errors
* @throws LambdaConversionException if there are improper conversions
*/
void validateMetafactoryArgs() throws LambdaConversionException {
- // Check target type is a subtype of class where SAM method is defined
- if (!samClass.isAssignableFrom(samBase)) {
- throw new LambdaConversionException(
- String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s",
- samBase.getName(), samClass.getName()));
- }
-
switch (implKind) {
case MethodHandleInfo.REF_invokeInterface:
case MethodHandleInfo.REF_invokeVirtual:
@@ -265,9 +268,9 @@
}
/**
- * Check type adaptability
- * @param fromType
- * @param toType
+ * 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'
*/
@@ -299,15 +302,14 @@
}
} else {
// both are reference types: fromType should be a superclass of toType.
- return strict? toType.isAssignableFrom(fromType) : true;
+ return !strict || toType.isAssignableFrom(fromType);
}
}
}
/**
- * Check type adaptability for return types -- special handling of void type) and parameterized fromType
- * @param fromType
- * @param toType
+ * 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) {
@@ -338,89 +340,4 @@
}
***********************/
- /**
- * 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;
- }
- }
}
--- a/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Jul 11 10:13:49 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Jul 11 14:02:20 2013 +0100
@@ -25,22 +25,26 @@
package java.lang.invoke;
+import jdk.internal.org.objectweb.asm.*;
+import sun.misc.Unsafe;
+
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.concurrent.atomic.AtomicInteger;
-import jdk.internal.org.objectweb.asm.*;
+
import static jdk.internal.org.objectweb.asm.Opcodes.*;
-import sun.misc.Unsafe;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
/**
- * Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite.
+ * Lambda metafactory implementation which dynamically creates an
+ * inner-class-like class per lambda callsite.
*
* @see LambdaMetafactory
*/
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
+ private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
private static final int CLASSFILE_VERSION = 51;
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
@@ -54,7 +58,7 @@
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
= MethodType.methodType(void.class,
Class.class,
- int.class, String.class, String.class, String.class,
+ String.class, String.class, String.class,
int.class, String.class, String.class, String.class,
String.class,
Object[].class).toMethodDescriptorString();
@@ -77,36 +81,56 @@
private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
/**
- * General meta-factory constructor, standard cases and allowing for uncommon options such as serialization.
+ * General meta-factory constructor, supporting both standard cases and
+ * allowing for uncommon options such as serialization or bridging.
*
- * @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 flags A bitmask containing flags that may influence the translation of this lambda expression. Defined
- * fields include FLAG_SERIALIZABLE.
- * @param markerInterfaces Additional interfaces which the lambda object should implement.
+ * @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 samMethodName Name of the method in the functional interface to
+ * which the lambda or method reference is being
+ * converted, represented as a String.
+ * @param samMethodType Type of the method in the functional interface to
+ * which the lambda or method reference is being
+ * converted, represented as a MethodType.
+ * @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
+ * @throws LambdaConversionException If any of the meta-factory protocol
+ * invariants are violated
*/
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
MethodType invokedType,
- MethodHandle samMethod,
+ String samMethodName,
+ MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType,
- int flags,
- Class<?>[] markerInterfaces)
+ boolean isSerializable,
+ Class<?>[] markerInterfaces,
+ MethodType[] additionalBridges)
throws ReflectiveOperationException, LambdaConversionException {
- super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces);
+ super(caller, invokedType, samMethodName, samMethodType,
+ implMethod, instantiatedMethodType,
+ isSerializable, markerInterfaces, additionalBridges);
implMethodClassName = implDefiningClass.getName().replace('.', '/');
implMethodName = implInfo.getName();
implMethodDesc = implMethodType.toMethodDescriptorString();
@@ -124,7 +148,8 @@
for (int i = 0; i < argTypes.length; i++) {
argNames[i] = "arg$" + (i + 1);
}
- instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
+ instantiatedArgumentTypes = Type.getArgumentTypes(
+ instantiatedMethodType.toMethodDescriptorString());
}
/**
@@ -136,7 +161,8 @@
* @return a CallSite, which, when invoked, will return an instance of the
* functional interface
* @throws ReflectiveOperationException
- * @throws LambdaConversionException If properly formed functional interface is not found
+ * @throws LambdaConversionException If properly formed functional interface
+ * is not found
*/
@Override
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
@@ -167,8 +193,8 @@
} else {
return new ConstantCallSite(
MethodHandles.Lookup.IMPL_LOOKUP
- .findConstructor(innerClass, constructorType)
- .asType(constructorType.changeReturnType(samBase)));
+ .findConstructor(innerClass, constructorType)
+ .asType(constructorType.changeReturnType(samBase)));
}
}
@@ -176,13 +202,20 @@
* Generate a class file which implements the functional
* interface, define and return the class.
*
+ * @implNote The class that is generated does not include signature
+ * information for exceptions that may be present on the SAM method.
+ * This is to reduce classfile size, and is harmless as checked exceptions
+ * are erased anyway, no one will ever compile against this classfile,
+ * and we make no guarantees about the reflective properties of lambda
+ * objects.
+ *
* @return a Class which implements the functional interface
- * @throws LambdaConversionException If properly formed functional interface is not found
+ * @throws LambdaConversionException If properly formed functional interface
+ * is not found
*/
private Class<?> spinInnerClass() throws LambdaConversionException {
- String samName = samBase.getName().replace('.', '/');
String[] interfaces = new String[markerInterfaces.length + 1];
- interfaces[0] = samName;
+ interfaces[0] = samBase.getName().replace('.', '/');
for (int i=0; i<markerInterfaces.length; i++) {
interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
}
@@ -192,35 +225,33 @@
// Generate final fields to be filled in by constructor
for (int i = 0; i < argTypes.length; i++) {
- FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(),
+ FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
+ argNames[i],
+ argTypes[i].getDescriptor(),
null, null);
fv.visitEnd();
}
generateConstructor();
- MethodAnalyzer ma = new MethodAnalyzer();
-
// Forward the SAM method
- if (ma.getSamMethod() == null) {
- throw new LambdaConversionException(String.format("Functional interface method not found: %s", samMethodType));
- } else {
- generateForwardingMethod(ma.getSamMethod(), false);
- }
+ String methodDescriptor = samMethodType.toMethodDescriptorString();
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
+ methodDescriptor, null, null);
+ new ForwardingMethodGenerator(mv).generate(methodDescriptor);
// Forward the bridges
- // @@@ The commented-out code is temporary, pending the VM's ability to bridge all methods on request
- // @@@ Once the VM can do fail-over, uncomment the !ma.wasDefaultMethodFound() test, and emit the appropriate
- // @@@ classfile attribute to request custom bridging. See 8002092.
- if (!ma.getMethodsToBridge().isEmpty() /* && !ma.conflictFoundBetweenDefaultAndBridge() */ ) {
- for (Method m : ma.getMethodsToBridge()) {
- generateForwardingMethod(m, true);
+ if (additionalBridges != null) {
+ for (MethodType mt : additionalBridges) {
+ methodDescriptor = mt.toMethodDescriptorString();
+ mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
+ methodDescriptor, null, null);
+ new ForwardingMethodGenerator(mv).generate(methodDescriptor);
}
}
- if (isSerializable) {
+ if (isSerializable)
generateWriteReplace();
- }
cw.visitEnd();
@@ -229,11 +260,14 @@
final byte[] classBytes = cw.toByteArray();
/*** Uncomment to dump the generated file
- System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
- try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
+ System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName,
+ classBytes.length);
+ try (FileOutputStream fos = new FileOutputStream(lambdaClassName
+ .replace('/', '.') + ".class")) {
fos.write(classBytes);
} catch (IOException ex) {
- PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName()).severe(ex.getMessage(), ex);
+ PlatformLogger.getLogger(InnerClassLambdaMetafactory.class
+ .getName()).severe(ex.getMessage(), ex);
}
***/
@@ -249,8 +283,9 @@
}
);
- return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
- loader, pd);
+ return UNSAFE.defineClass(lambdaClassName,
+ classBytes, 0, classBytes.length,
+ loader, pd);
}
/**
@@ -258,19 +293,23 @@
*/
private void generateConstructor() {
// Generate constructor
- MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, constructorDesc, null, null);
+ MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
+ constructorDesc, null, null);
ctor.visitCode();
ctor.visitVarInsn(ALOAD, 0);
- ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, METHOD_DESCRIPTOR_VOID);
+ ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR,
+ METHOD_DESCRIPTOR_VOID);
int lvIndex = 0;
for (int i = 0; i < argTypes.length; i++) {
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
lvIndex += argTypes[i].getSize();
- ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i],
+ argTypes[i].getDescriptor());
}
ctor.visitInsn(RETURN);
- ctor.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ ctor.visitMaxs(-1, -1);
ctor.visitEnd();
}
@@ -279,18 +318,18 @@
*/
private void generateWriteReplace() {
TypeConvertingMethodAdapter mv
- = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
- NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
- null, null));
+ = new TypeConvertingMethodAdapter(
+ cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
+ NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
+ null, null));
mv.visitCode();
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
- mv.visitInsn(DUP);;
+ mv.visitInsn(DUP);
mv.visitLdcInsn(Type.getType(targetClass));
- mv.visitLdcInsn(samInfo.getReferenceKind());
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
- mv.visitLdcInsn(samInfo.getName());
- mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString());
+ mv.visitLdcInsn(samMethodName);
+ mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
mv.visitLdcInsn(implInfo.getReferenceKind());
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
mv.visitLdcInsn(implInfo.getName());
@@ -303,36 +342,20 @@
mv.visitInsn(DUP);
mv.iconst(i);
mv.visitVarInsn(ALOAD, 0);
- mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
+ argTypes[i].getDescriptor());
mv.boxIfTypePrimitive(argTypes[i]);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
DESCR_CTOR_SERIALIZED_LAMBDA);
mv.visitInsn(ARETURN);
- mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ mv.visitMaxs(-1, -1);
mv.visitEnd();
}
/**
- * Generate a method which calls the lambda implementation method,
- * converting arguments, as needed.
- * @param m The method whose signature should be generated
- * @param isBridge True if this methods should be flagged as a bridge
- */
- private void generateForwardingMethod(Method m, boolean isBridge) {
- Class<?>[] exceptionTypes = m.getExceptionTypes();
- String[] exceptionNames = new String[exceptionTypes.length];
- for (int i = 0; i < exceptionTypes.length; i++) {
- exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
- }
- String methodDescriptor = Type.getMethodDescriptor(m);
- int access = isBridge? ACC_PUBLIC | ACC_BRIDGE : ACC_PUBLIC;
- MethodVisitor mv = cw.visitMethod(access, m.getName(), methodDescriptor, null, exceptionNames);
- new ForwardingMethodGenerator(mv).generate(m);
- }
-
- /**
* This class generates a method body which calls the lambda implementation
* method, converting arguments, as needed.
*/
@@ -342,36 +365,39 @@
super(mv);
}
- void generate(Method m) throws InternalError {
+ void generate(String methodDescriptor) {
visitCode();
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
visitTypeInsn(NEW, implMethodClassName);
- visitInsn(DUP);;
+ visitInsn(DUP);
}
for (int i = 0; i < argTypes.length; i++) {
visitVarInsn(ALOAD, 0);
- visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
+ argTypes[i].getDescriptor());
}
- convertArgumentTypes(Type.getArgumentTypes(m));
+ convertArgumentTypes(Type.getArgumentTypes(methodDescriptor));
// Invoke the method we want to forward to
visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
// Convert the return value (if any) and return it
- // Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
- Type samReturnType = Type.getReturnType(m);
+ // Note: if adapting from non-void to void, the 'return'
+ // instruction will pop the unneeded result
+ Type samReturnType = Type.getReturnType(methodDescriptor);
convertType(implMethodReturnType, samReturnType, samReturnType);
visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
-
- visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
+ visitMaxs(-1, -1);
visitEnd();
}
private void convertArgumentTypes(Type[] samArgumentTypes) {
int lvIndex = 0;
- boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0;
+ boolean samIncludesReceiver = implIsInstanceMethod &&
+ argTypes.length == 0;
int samReceiverLength = samIncludesReceiver ? 1 : 0;
if (samIncludesReceiver) {
// push receiver
@@ -395,7 +421,9 @@
}
private void convertType(Type argType, Type targetType, Type functionalType) {
- convertType(argType.getDescriptor(), targetType.getDescriptor(), functionalType.getDescriptor());
+ convertType(argType.getDescriptor(),
+ targetType.getDescriptor(),
+ functionalType.getDescriptor());
}
private int invocationOpcode() throws InternalError {
--- a/jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java Thu Jul 11 10:13:49 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java Thu Jul 11 14:02:20 2013 +0100
@@ -25,6 +25,9 @@
package java.lang.invoke;
+import java.io.Serializable;
+import java.util.Arrays;
+
/**
* <p>Bootstrap methods for converting lambda expressions and method references to functional interface objects.</p>
*
@@ -44,16 +47,11 @@
*
* <p>When parameterized types are used, the instantiated type of the functional interface method may be different
* from that in the functional interface. For example, consider
- * <code>interface I<T> { int m(T x); }</code> if this functional interface type is used in a lambda
- * <code>I<Byte> v = ...</code>, we need both the actual functional interface method which has the signature
- * <code>(Object)int</code> and the erased instantiated type of the functional interface method (or simply
+ * {@code interface I<T> { int m(T x); }} if this functional interface type is used in a lambda
+ * {@code I<Byte>; v = ...}, we need both the actual functional interface method which has the signature
+ * {@code (Object)int} and the erased instantiated type of the functional interface method (or simply
* <I>instantiated method type</I>), which has signature
- * <code>(Byte)int</code>.
- *
- * <p>While functional interfaces only have a single abstract method from the language perspective (concrete
- * methods in Object are and default methods may be present), at the bytecode level they may actually have multiple
- * methods because of the need for bridge methods. Invoking any of these methods on the lambda object will result
- * in invoking the implementation method.
+ * {@code (Byte)int}.
*
* <p>The argument list of the implementation method and the argument list of the functional interface method(s)
* may differ in several ways. The implementation methods may have additional arguments to accommodate arguments
@@ -137,108 +135,147 @@
* </tr>
* </table>
*
- * The default bootstrap ({@link #metaFactory}) represents the common cases and uses an optimized protocol.
- * Alternate bootstraps (e.g., {@link #altMetaFactory}) exist to support uncommon cases such as serialization
+ * The default bootstrap ({@link #metafactory}) represents the common cases and uses an optimized protocol.
+ * Alternate bootstraps (e.g., {@link #altMetafactory}) exist to support uncommon cases such as serialization
* or additional marker superinterfaces.
*
*/
public class LambdaMetafactory {
- /** Flag for alternate metafactories indicating the lambda object is must to be serializable */
+ /** Flag for alternate metafactories indicating the lambda object is
+ * must to be serializable */
public static final int FLAG_SERIALIZABLE = 1 << 0;
/**
- * Flag for alternate metafactories indicating the lambda object implements other marker interfaces
+ * Flag for alternate metafactories indicating the lambda object implements
+ * other marker interfaces
* besides Serializable
*/
public static final int FLAG_MARKERS = 1 << 1;
+ /**
+ * Flag for alternate metafactories indicating the lambda object requires
+ * additional bridge methods
+ */
+ public static final int FLAG_BRIDGES = 1 << 2;
+
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+ private static final MethodType[] EMPTY_MT_ARRAY = new MethodType[0];
/**
- * Standard meta-factory for conversion of lambda expressions or method references to functional interfaces.
+ * Standard meta-factory for conversion of lambda expressions or method
+ * references to functional interfaces.
*
- * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
- * of the caller.
- * @param invokedName Stacked automatically by VM; the name of the invoked method as it appears at the call site.
- * Currently unused.
- * @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
- * @return a CallSite, which, when invoked, will return an instance of the functional interface
- * @throws ReflectiveOperationException if the caller is not able to reconstruct one of the method handles
- * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
+ * @param caller Stacked automatically by VM; represents a lookup context
+ * with the accessibility privileges of the caller.
+ * @param invokedName Stacked automatically by VM; the name of the invoked
+ * method as it appears at the call site.
+ * Used as the name of the functional interface method
+ * to which the lambda or method reference is being
+ * converted.
+ * @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 samMethodType MethodType of the method in the functional interface
+ * to which the lambda or method reference is being
+ * converted, represented as a MethodType.
+ * @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
+ * @return a CallSite, which, when invoked, will return an instance of the
+ * functional interface
+ * @throws ReflectiveOperationException if the caller is not able to
+ * reconstruct one of the method handles
+ * @throws LambdaConversionException If any of the meta-factory protocol
+ * invariants are violated
*/
- public static CallSite metaFactory(MethodHandles.Lookup caller,
+ public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
- MethodHandle samMethod,
+ MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws ReflectiveOperationException, LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
- mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType,
- 0, EMPTY_CLASS_ARRAY);
+ mf = new InnerClassLambdaMetafactory(caller, invokedType,
+ invokedName, samMethodType,
+ implMethod, instantiatedMethodType,
+ false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
/**
- * Alternate meta-factory for conversion of lambda expressions or method references to functional interfaces,
- * which supports serialization and other uncommon options.
+ * Alternate meta-factory for conversion of lambda expressions or method
+ * references to functional interfaces, which supports serialization and
+ * other uncommon options.
*
* The declared argument list for this method is:
*
- * CallSite altMetaFactory(MethodHandles.Lookup caller,
+ * CallSite altMetafactory(MethodHandles.Lookup caller,
* String invokedName,
* MethodType invokedType,
* Object... args)
*
* but it behaves as if the argument list is:
*
- * CallSite altMetaFactory(MethodHandles.Lookup caller,
+ * CallSite altMetafactory(MethodHandles.Lookup caller,
* String invokedName,
* MethodType invokedType,
- * MethodHandle samMethod
+ * MethodType samMethodType
* MethodHandle implMethod,
* MethodType instantiatedMethodType,
* int flags,
* int markerInterfaceCount, // IF flags has MARKERS set
* Class... markerInterfaces // IF flags has MARKERS set
+ * int bridgeCount, // IF flags has BRIDGES set
+ * MethodType... bridges // IF flags has BRIDGES set
* )
*
*
- * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
- * of the caller.
- * @param invokedName Stacked automatically by VM; the name of the invoked method as it appears at the call site.
- * Currently unused.
- * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes thefu
- * 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 args argument to pass, flags, marker interface count, and marker interfaces as described above
- * @return a CallSite, which, when invoked, will return an instance of the functional interface
- * @throws ReflectiveOperationException if the caller is not able to reconstruct one of the method handles
- * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
+ * @param caller Stacked automatically by VM; represents a lookup context
+ * with the accessibility privileges of the caller.
+ * @param invokedName Stacked automatically by VM; the name of the invoked
+ * method as it appears at the call site.
+ * Used as the name of the functional interface method
+ * to which the lambda or method reference is being
+ * converted.
+ * @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 args flags and optional arguments, as described above
+ * @return a CallSite, which, when invoked, will return an instance of the
+ * functional interface
+ * @throws ReflectiveOperationException if the caller is not able to
+ * reconstruct one of the method handles
+ * @throws LambdaConversionException If any of the meta-factory protocol
+ * invariants are violated
*/
- public static CallSite altMetaFactory(MethodHandles.Lookup caller,
+ public static CallSite altMetafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
Object... args)
throws ReflectiveOperationException, LambdaConversionException {
- MethodHandle samMethod = (MethodHandle)args[0];
+ MethodType samMethodType = (MethodType)args[0];
MethodHandle implMethod = (MethodHandle)args[1];
MethodType instantiatedMethodType = (MethodType)args[2];
int flags = (Integer) args[3];
Class<?>[] markerInterfaces;
+ MethodType[] bridges;
int argIndex = 4;
if ((flags & FLAG_MARKERS) != 0) {
int markerCount = (Integer) args[argIndex++];
@@ -248,9 +285,33 @@
}
else
markerInterfaces = EMPTY_CLASS_ARRAY;
- AbstractValidatingLambdaMetafactory mf;
- mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType,
- flags, markerInterfaces);
+ if ((flags & FLAG_BRIDGES) != 0) {
+ int bridgeCount = (Integer) args[argIndex++];
+ bridges = new MethodType[bridgeCount];
+ System.arraycopy(args, argIndex, bridges, 0, bridgeCount);
+ argIndex += bridgeCount;
+ }
+ else
+ bridges = EMPTY_MT_ARRAY;
+
+ boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(invokedType.returnType());
+ for (Class<?> c : markerInterfaces)
+ foundSerializableSupertype |= Serializable.class.isAssignableFrom(c);
+ boolean isSerializable = ((flags & LambdaMetafactory.FLAG_SERIALIZABLE) != 0)
+ || foundSerializableSupertype;
+
+ if (isSerializable && !foundSerializableSupertype) {
+ markerInterfaces = Arrays.copyOf(markerInterfaces, markerInterfaces.length + 1);
+ markerInterfaces[markerInterfaces.length-1] = Serializable.class;
+ }
+
+ AbstractValidatingLambdaMetafactory mf
+ = new InnerClassLambdaMetafactory(caller, invokedType,
+ invokedName, samMethodType,
+ implMethod,
+ instantiatedMethodType,
+ isSerializable,
+ markerInterfaces, bridges);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
--- a/jdk/src/share/classes/java/lang/invoke/SerializedLambda.java Thu Jul 11 10:13:49 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/SerializedLambda.java Thu Jul 11 14:02:20 2013 +0100
@@ -44,7 +44,6 @@
private final String functionalInterfaceClass;
private final String functionalInterfaceMethodName;
private final String functionalInterfaceMethodSignature;
- private final int functionalInterfaceMethodKind;
private final String implClass;
private final String implMethodName;
private final String implMethodSignature;
@@ -53,28 +52,32 @@
private final Object[] capturedArgs;
/**
- * Create a {@code SerializedLambda} from the low-level information present at the lambda factory site.
+ * Create a {@code SerializedLambda} from the low-level information present
+ * at the lambda factory site.
*
* @param capturingClass The class in which the lambda expression appears
- * @param functionalInterfaceMethodKind Method handle kind (see {@link MethodHandleInfo}) for the
- * functional interface method handle present at the lambda factory site
- * @param functionalInterfaceClass Name, in slash-delimited form, for the functional interface class present at the
- * lambda factory site
- * @param functionalInterfaceMethodName Name of the primary method for the functional interface present at the
+ * @param functionalInterfaceClass Name, in slash-delimited form, of static
+ * type of the returned lambda object
+ * @param functionalInterfaceMethodName Name of the functional interface
+ * method for the present at the
* lambda factory site
- * @param functionalInterfaceMethodSignature Signature of the primary method for the functional interface present
- * at the lambda factory site
+ * @param functionalInterfaceMethodSignature Signature of the functional
+ * interface method present at
+ * the lambda factory site
* @param implMethodKind Method handle kind for the implementation method
- * @param implClass Name, in slash-delimited form, for the class holding the implementation method
+ * @param implClass Name, in slash-delimited form, for the class holding
+ * the implementation method
* @param implMethodName Name of the implementation method
* @param implMethodSignature Signature of the implementation method
- * @param instantiatedMethodType The signature of the primary functional interface method after type variables
- * are substituted with their instantiation from the capture site
- * @param capturedArgs The dynamic arguments to the lambda factory site, which represent variables captured by
+ * @param instantiatedMethodType The signature of the primary functional
+ * interface method after type variables
+ * are substituted with their instantiation
+ * from the capture site
+ * @param capturedArgs The dynamic arguments to the lambda factory site,
+ * which represent variables captured by
* the lambda
*/
public SerializedLambda(Class<?> capturingClass,
- int functionalInterfaceMethodKind,
String functionalInterfaceClass,
String functionalInterfaceMethodName,
String functionalInterfaceMethodSignature,
@@ -85,7 +88,6 @@
String instantiatedMethodType,
Object[] capturedArgs) {
this.capturingClass = capturingClass;
- this.functionalInterfaceMethodKind = functionalInterfaceMethodKind;
this.functionalInterfaceClass = functionalInterfaceClass;
this.functionalInterfaceMethodName = functionalInterfaceMethodName;
this.functionalInterfaceMethodSignature = functionalInterfaceMethodSignature;
@@ -106,10 +108,10 @@
}
/**
- * Get the name of the functional interface class to which this
+ * Get the name of the invoked type to which this
* lambda has been converted
- * @return the name of the functional interface this lambda has
- * been converted to
+ * @return the name of the functional interface class to which
+ * this lambda has been converted
*/
public String getFunctionalInterfaceClass() {
return functionalInterfaceClass;
@@ -135,17 +137,6 @@
}
/**
- * Get the method handle kind (see {@link MethodHandleInfo}) of
- * the primary method for the functional interface to which this
- * lambda has been converted
- * @return the method handle kind of the primary method of
- * functional interface
- */
- public int getFunctionalInterfaceMethodKind() {
- return functionalInterfaceMethodKind;
- }
-
- /**
* Get the name of the class containing the implementation
* method.
* @return the name of the class containing the implementation
@@ -234,11 +225,17 @@
@Override
public String toString() {
- return String.format("SerializedLambda[capturingClass=%s, functionalInterfaceMethod=%s %s.%s:%s, " +
- "implementation=%s %s.%s:%s, instantiatedMethodType=%s, numCaptured=%d]",
- capturingClass, MethodHandleInfo.getReferenceKindString(functionalInterfaceMethodKind),
- functionalInterfaceClass, functionalInterfaceMethodName, functionalInterfaceMethodSignature,
- MethodHandleInfo.getReferenceKindString(implMethodKind), implClass, implMethodName,
- implMethodSignature, instantiatedMethodType, capturedArgs.length);
+ String implKind=MethodHandleInfo.getReferenceKindString(implMethodKind);
+ return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
+ "%s=%s %s.%s:%s, %s=%s, %s=%d]",
+ "capturingClass", capturingClass,
+ "functionalInterfaceMethod", functionalInterfaceClass,
+ functionalInterfaceMethodName,
+ functionalInterfaceMethodSignature,
+ "implementation",
+ implKind,
+ implClass, implMethodName, implMethodSignature,
+ "instantiatedMethodType", instantiatedMethodType,
+ "numCaptured", capturedArgs.length);
}
}