8004970: Implement serialization in the lambda metafactory
Reviewed-by: forax
This commit is contained in:
parent
5c9a25d2d2
commit
381aecb7c2
@ -34,11 +34,11 @@ import sun.invoke.util.Wrapper;
|
||||
import static sun.invoke.util.Wrapper.*;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a meta-factory which provides parameter unrolling and input validation.
|
||||
* Abstract implementation of a lambda metafactory which provides parameter unrolling and input validation.
|
||||
*
|
||||
* @author Robert Field
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/*non-public*/ abstract class AbstractValidatingLambdaMetafactory {
|
||||
/* package */ abstract class AbstractValidatingLambdaMetafactory {
|
||||
|
||||
/*
|
||||
* For context, the comments for the following fields are marked in quotes with their values, given this program:
|
||||
@ -54,16 +54,19 @@ import static sun.invoke.util.Wrapper.*;
|
||||
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 boolean isSerializable; // Should the returned instance be serializable
|
||||
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 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]"
|
||||
final int implKind; // Invocation kind for implementation "5"=invokevirtual
|
||||
final boolean implIsInstanceMethod; // Is the implementation an instance method "true"
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
@ -80,27 +83,35 @@ import static sun.invoke.util.Wrapper.*;
|
||||
* @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 SAM method from the functional interface's perspective
|
||||
* @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)
|
||||
throws ReflectiveOperationException {
|
||||
MethodType instantiatedMethodType,
|
||||
int flags,
|
||||
Class<?>[] markerInterfaces)
|
||||
throws ReflectiveOperationException, LambdaConversionException {
|
||||
this.targetClass = caller.lookupClass();
|
||||
this.invokedType = invokedType;
|
||||
|
||||
this.samBase = invokedType.returnType();
|
||||
this.isSerializable = Serializable.class.isAssignableFrom(samBase);
|
||||
|
||||
this.samMethod = samMethod;
|
||||
this.samInfo = new MethodHandleInfo(samMethod);
|
||||
this.samClass = samInfo.getDeclaringClass();
|
||||
this.samMethodType = samInfo.getMethodType();
|
||||
|
||||
this.implMethod = implMethod;
|
||||
this.implInfo = new MethodHandleInfo(implMethod);
|
||||
this.implKind = implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial? MethodHandleInfo.REF_invokeVirtual : implInfo.getReferenceKind(); // @@@ Temp work-around to hotspot incorrectly converting to invokespecial
|
||||
// @@@ Temporary work-around pending resolution of 8005119
|
||||
this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
|
||||
? MethodHandleInfo.REF_invokeVirtual
|
||||
: implInfo.getReferenceKind();
|
||||
this.implIsInstanceMethod =
|
||||
implKind == MethodHandleInfo.REF_invokeVirtual ||
|
||||
implKind == MethodHandleInfo.REF_invokeSpecial ||
|
||||
@ -109,6 +120,30 @@ import static sun.invoke.util.Wrapper.*;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,8 +162,9 @@ import static sun.invoke.util.Wrapper.*;
|
||||
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()));
|
||||
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) {
|
||||
@ -149,14 +185,16 @@ import static sun.invoke.util.Wrapper.*;
|
||||
final int samArity = samMethodType.parameterCount();
|
||||
final int instantiatedArity = instantiatedMethodType.parameterCount();
|
||||
if (implArity + receiverArity != capturedArity + samArity) {
|
||||
throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface parameters, %d implementation parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
capturedArity, samArity, implArity));
|
||||
throw new LambdaConversionException(
|
||||
String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface method parameters, %d implementation parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
capturedArity, samArity, implArity));
|
||||
}
|
||||
if (instantiatedArity != samArity) {
|
||||
throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d functional interface parameters, %d SAM method parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
instantiatedArity, samArity));
|
||||
throw new LambdaConversionException(
|
||||
String.format("Incorrect number of parameters for %s method %s; %d instantiated parameters, %d functional interface method parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
instantiatedArity, samArity));
|
||||
}
|
||||
|
||||
// If instance: first captured arg (receiver) must be subtype of class where impl method is defined
|
||||
@ -180,8 +218,9 @@ import static sun.invoke.util.Wrapper.*;
|
||||
|
||||
// check receiver type
|
||||
if (!implDefiningClass.isAssignableFrom(receiverClass)) {
|
||||
throw new LambdaConversionException(String.format("Invalid receiver type %s; not a subtype of implementation type %s",
|
||||
receiverClass, implDefiningClass));
|
||||
throw new LambdaConversionException(
|
||||
String.format("Invalid receiver type %s; not a subtype of implementation type %s",
|
||||
receiverClass, implDefiningClass));
|
||||
}
|
||||
} else {
|
||||
// no receiver
|
||||
@ -196,7 +235,8 @@ import static sun.invoke.util.Wrapper.*;
|
||||
Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
|
||||
if (!capturedParamType.equals(implParamType)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s", i, capturedParamType, implParamType));
|
||||
String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
|
||||
i, capturedParamType, implParamType));
|
||||
}
|
||||
}
|
||||
// Check for adaptation match on SAM arguments
|
||||
@ -206,7 +246,8 @@ import static sun.invoke.util.Wrapper.*;
|
||||
Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
|
||||
if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", i, instantiatedParamType, implParamType));
|
||||
String.format("Type mismatch for lambda argument %d: %s is not convertible to %s",
|
||||
i, instantiatedParamType, implParamType));
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +259,8 @@ import static sun.invoke.util.Wrapper.*;
|
||||
: implMethodType.returnType();
|
||||
if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType));
|
||||
String.format("Type mismatch for lambda return: %s is not convertible to %s",
|
||||
actualReturnType, expectedType));
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,8 +316,8 @@ import static sun.invoke.util.Wrapper.*;
|
||||
}
|
||||
|
||||
|
||||
/*********** Logging support -- for debugging only
|
||||
static final Executor logPool = Executors.newSingleThreadExecutor(); // @@@ For debugging only
|
||||
/*********** Logging support -- for debugging only, uncomment as needed
|
||||
static final Executor logPool = Executors.newSingleThreadExecutor();
|
||||
protected static void log(final String s) {
|
||||
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||
@Override
|
||||
@ -297,17 +339,21 @@ import static sun.invoke.util.Wrapper.*;
|
||||
***********************/
|
||||
|
||||
/**
|
||||
* Find the SAM method and corresponding methods which should be bridged. SAM method and those to be bridged
|
||||
* will have the same name and number of parameters. Check for matching default methods (non-abstract), they
|
||||
* should not be bridged-over and indicate a complex bridging situation.
|
||||
* 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 final List<Method> methodsFound = new ArrayList<>(methods.length);
|
||||
|
||||
private Method samMethod = null;
|
||||
private final List<Method> methodsToBridge = new ArrayList<>(methods.length);
|
||||
private boolean defaultMethodFound = false;
|
||||
private boolean conflictFoundBetweenDefaultAndBridge = false;
|
||||
|
||||
MethodAnalyzer() {
|
||||
String samMethodName = samInfo.getName();
|
||||
@ -315,31 +361,36 @@ import static sun.invoke.util.Wrapper.*;
|
||||
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())) {
|
||||
// Exclude methods with duplicate signatures
|
||||
if (methodUnique(m)) {
|
||||
if (m.getReturnType().equals(samReturnType) && Arrays.equals(mParamTypes, samParamTypes)) {
|
||||
// Exact match, this is the SAM method signature
|
||||
samMethod = m;
|
||||
} else {
|
||||
methodsToBridge.add(m);
|
||||
}
|
||||
// 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 {
|
||||
// This is a default method, flag for special processing
|
||||
defaultMethodFound = true;
|
||||
// Ignore future matching abstracts.
|
||||
// Note, due to reabstraction, this is really a punt, hence pass-off to VM
|
||||
methodUnique(m);
|
||||
// Record default methods for conflict testing
|
||||
defaultMethods.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Method dm : defaultMethods) {
|
||||
if (hasMatchingBridgeSignature(dm)) {
|
||||
conflictFoundBetweenDefaultAndBridge = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method getSamMethod() {
|
||||
@ -350,27 +401,26 @@ import static sun.invoke.util.Wrapper.*;
|
||||
return methodsToBridge;
|
||||
}
|
||||
|
||||
boolean wasDefaultMethodFound() {
|
||||
return defaultMethodFound;
|
||||
boolean conflictFoundBetweenDefaultAndBridge() {
|
||||
return conflictFoundBetweenDefaultAndBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the list of previously found methods to determine if there is a method with the same signature
|
||||
* (return and parameter types) as the specified method. If it wasn't found before, add to the found list.
|
||||
* 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 False if the method was found, True otherwise
|
||||
* @return True if the method was found, False otherwise
|
||||
*/
|
||||
private boolean methodUnique(Method m) {
|
||||
private boolean hasMatchingBridgeSignature(Method m) {
|
||||
Class<?>[] ptypes = m.getParameterTypes();
|
||||
Class<?> rtype = m.getReturnType();
|
||||
for (Method md : methodsFound) {
|
||||
for (Method md : methodsToBridge) {
|
||||
if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
methodsFound.add(m);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,21 +36,28 @@ import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* InnerClassLambdaMetafactory
|
||||
* Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite.
|
||||
*
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/*non-public*/ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final int CLASSFILE_VERSION = 51;
|
||||
private static final Type TYPE_VOID = Type.getType(void.class);
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
|
||||
private static final String NAME_SERIALIZABLE = "java/io/Serializable";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
|
||||
//Serialization support
|
||||
private static final String NAME_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl";
|
||||
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
||||
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
||||
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
||||
private static final String NAME_OBJECT = "java/lang/Object";
|
||||
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
||||
= MethodType.methodType(void.class,
|
||||
String.class,
|
||||
int.class, String.class, String.class, String.class,
|
||||
int.class, String.class, String.class, String.class,
|
||||
String.class,
|
||||
Object[].class).toMethodDescriptorString();
|
||||
|
||||
// Used to ensure that each spun class name is unique
|
||||
private static final AtomicInteger counter = new AtomicInteger(0);
|
||||
@ -70,7 +77,7 @@ import java.security.PrivilegedAction;
|
||||
private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
|
||||
|
||||
/**
|
||||
* Meta-factory constructor.
|
||||
* General meta-factory constructor, standard cases and allowing for uncommon options such as serialization.
|
||||
*
|
||||
* @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
|
||||
* of the caller.
|
||||
@ -83,16 +90,23 @@ import java.security.PrivilegedAction;
|
||||
* @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 SAM method from the functional interface's perspective
|
||||
* @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.
|
||||
* @throws ReflectiveOperationException
|
||||
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
||||
*/
|
||||
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
||||
MethodType invokedType,
|
||||
MethodHandle samMethod,
|
||||
MethodHandle implMethod,
|
||||
MethodType instantiatedMethodType)
|
||||
throws ReflectiveOperationException {
|
||||
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
|
||||
MethodType instantiatedMethodType,
|
||||
int flags,
|
||||
Class<?>[] markerInterfaces)
|
||||
throws ReflectiveOperationException, LambdaConversionException {
|
||||
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces);
|
||||
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
||||
implMethodName = implInfo.getName();
|
||||
implMethodDesc = implMethodType.toMethodDescriptorString();
|
||||
@ -109,7 +123,6 @@ import java.security.PrivilegedAction;
|
||||
argNames[i] = "arg$" + (i + 1);
|
||||
}
|
||||
instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +133,8 @@ import java.security.PrivilegedAction;
|
||||
*
|
||||
* @return a CallSite, which, when invoked, will return an instance of the
|
||||
* functional interface
|
||||
* @throws ReflectiveOperationException, LambdaConversionException
|
||||
* @throws ReflectiveOperationException
|
||||
* @throws LambdaConversionException If properly formed functional interface is not found
|
||||
*/
|
||||
@Override
|
||||
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
|
||||
@ -151,8 +165,8 @@ import java.security.PrivilegedAction;
|
||||
} else {
|
||||
return new ConstantCallSite(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP
|
||||
.findConstructor(innerClass, constructorType)
|
||||
.asType(constructorType.changeReturnType(samBase)));
|
||||
.findConstructor(innerClass, constructorType)
|
||||
.asType(constructorType.changeReturnType(samBase)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,16 +175,23 @@ import java.security.PrivilegedAction;
|
||||
* interface, define and return the class.
|
||||
*
|
||||
* @return a Class which implements the functional interface
|
||||
* @throws LambdaConversionException If properly formed functional interface is not found
|
||||
*/
|
||||
private <T> Class<? extends T> spinInnerClass() throws LambdaConversionException {
|
||||
private Class<?> spinInnerClass() throws LambdaConversionException {
|
||||
String samName = samBase.getName().replace('.', '/');
|
||||
|
||||
cw.visit(CLASSFILE_VERSION, ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
|
||||
isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
|
||||
String[] interfaces = new String[markerInterfaces.length + 1];
|
||||
interfaces[0] = samName;
|
||||
for (int i=0; i<markerInterfaces.length; i++) {
|
||||
interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
|
||||
}
|
||||
cw.visit(CLASSFILE_VERSION, ACC_SUPER,
|
||||
lambdaClassName, null,
|
||||
NAME_MAGIC_ACCESSOR_IMPL, interfaces);
|
||||
|
||||
// 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(), null, null);
|
||||
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(),
|
||||
null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
@ -180,26 +201,24 @@ import java.security.PrivilegedAction;
|
||||
|
||||
// Forward the SAM method
|
||||
if (ma.getSamMethod() == null) {
|
||||
throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType));
|
||||
throw new LambdaConversionException(String.format("Functional interface method not found: %s", samMethodType));
|
||||
} else {
|
||||
generateForwardingMethod(ma.getSamMethod(), false);
|
||||
}
|
||||
|
||||
// Forward the bridges
|
||||
// @@@ Once the VM can do fail-over, uncomment the default method test
|
||||
if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) {
|
||||
// @@@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/***** Serialization not yet supported
|
||||
if (isSerializable) {
|
||||
String samMethodName = samInfo.getName();
|
||||
Type samType = Type.getType(samBase);
|
||||
generateSerializationMethod(samType, samMethodName);
|
||||
generateWriteReplace();
|
||||
}
|
||||
******/
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
@ -212,7 +231,7 @@ import java.security.PrivilegedAction;
|
||||
try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
|
||||
fos.write(classBytes);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(InnerClassLambdaMetafactory.class.getName()).log(Level.SEVERE, null, ex);
|
||||
PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName()).severe(ex.getMessage(), ex);
|
||||
}
|
||||
***/
|
||||
|
||||
@ -228,7 +247,8 @@ import java.security.PrivilegedAction;
|
||||
}
|
||||
);
|
||||
|
||||
return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
|
||||
return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
|
||||
loader, pd);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,40 +273,44 @@ import java.security.PrivilegedAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the serialization method (if needed)
|
||||
* Generate the writeReplace method (if needed for serialization)
|
||||
*/
|
||||
/****** This code is out of date -- known to be wrong -- and not currently used ******
|
||||
private void generateSerializationMethod(Type samType, String samMethodName) {
|
||||
String samMethodDesc = samMethodType.toMethodDescriptorString();
|
||||
TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null));
|
||||
private void generateWriteReplace() {
|
||||
TypeConvertingMethodAdapter mv
|
||||
= 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.dup();
|
||||
mv.visitLdcInsn(samType);
|
||||
mv.visitLdcInsn(samMethodName);
|
||||
mv.visitLdcInsn(samMethodDesc);
|
||||
mv.visitLdcInsn(Type.getType(implDefiningClass));
|
||||
mv.visitLdcInsn(implMethodName);
|
||||
mv.visitLdcInsn(implMethodDesc);
|
||||
mv.visitInsn(DUP);;
|
||||
mv.visitLdcInsn(targetClass.getName().replace('.', '/'));
|
||||
mv.visitLdcInsn(samInfo.getReferenceKind());
|
||||
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
|
||||
mv.visitLdcInsn(samInfo.getName());
|
||||
mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString());
|
||||
mv.visitLdcInsn(implInfo.getReferenceKind());
|
||||
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
|
||||
mv.visitLdcInsn(implInfo.getName());
|
||||
mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
|
||||
mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
|
||||
|
||||
mv.iconst(argTypes.length);
|
||||
mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
mv.dup();
|
||||
mv.visitInsn(DUP);
|
||||
mv.iconst(i);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||
mv.boxIfPrimitive(argTypes[i]);
|
||||
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||
mv.boxIfTypePrimitive(argTypes[i]);
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
mv.invokespecial(NAME_SERIALIZED_LAMBDA, NAME_CTOR,
|
||||
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
|
||||
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
|
||||
mv.visitEnd();
|
||||
}
|
||||
********/
|
||||
|
||||
/**
|
||||
* Generate a method which calls the lambda implementation method,
|
||||
@ -321,11 +345,11 @@ import java.security.PrivilegedAction;
|
||||
|
||||
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
||||
visitTypeInsn(NEW, implMethodClassName);
|
||||
dup();
|
||||
visitInsn(DUP);;
|
||||
}
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||
}
|
||||
|
||||
convertArgumentTypes(Type.getArgumentTypes(m));
|
||||
@ -337,7 +361,7 @@ import java.security.PrivilegedAction;
|
||||
// Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
|
||||
Type samReturnType = Type.getReturnType(m);
|
||||
convertType(implMethodReturnType, samReturnType, samReturnType);
|
||||
areturn(samReturnType);
|
||||
visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
|
||||
|
||||
visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||
visitEnd();
|
||||
@ -352,7 +376,7 @@ import java.security.PrivilegedAction;
|
||||
Type rcvrType = samArgumentTypes[0];
|
||||
Type instantiatedRcvrType = instantiatedArgumentTypes[0];
|
||||
|
||||
load(lvIndex + 1, rcvrType);
|
||||
visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
|
||||
lvIndex += rcvrType.getSize();
|
||||
convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
|
||||
}
|
||||
@ -362,7 +386,7 @@ import java.security.PrivilegedAction;
|
||||
Type targetType = implMethodArgumentTypes[argOffset + i];
|
||||
Type instantiatedArgType = instantiatedArgumentTypes[i];
|
||||
|
||||
load(lvIndex + 1, argType);
|
||||
visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
|
||||
lvIndex += argType.getSize();
|
||||
convertType(argType, targetType, instantiatedArgType);
|
||||
}
|
||||
@ -388,45 +412,5 @@ import java.security.PrivilegedAction;
|
||||
throw new InternalError("Unexpected invocation kind: " + implKind);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following methods are copied from
|
||||
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very
|
||||
* small and fast Java bytecode manipulation framework. Copyright (c)
|
||||
* 2000-2005 INRIA, France Telecom All rights reserved.
|
||||
*
|
||||
* Subclass with that (removing these methods) if that package/class is
|
||||
* ever added to the JDK.
|
||||
*/
|
||||
private void iconst(final int cst) {
|
||||
if (cst >= -1 && cst <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, cst);
|
||||
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, cst);
|
||||
} else {
|
||||
mv.visitLdcInsn(cst);
|
||||
}
|
||||
}
|
||||
|
||||
private void load(final int var, final Type type) {
|
||||
mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
|
||||
}
|
||||
|
||||
private void dup() {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
}
|
||||
|
||||
private void areturn(final Type t) {
|
||||
mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
|
||||
}
|
||||
|
||||
private void getfield(
|
||||
final String owner,
|
||||
final String name,
|
||||
final String desc) {
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,14 +42,13 @@ package java.lang.invoke;
|
||||
* method, and the static types of the captured lambda arguments, and link a call site which, when invoked,
|
||||
* produces the lambda object.
|
||||
*
|
||||
* <p>Two pieces of information are needed about the functional interface: the SAM method and the type of the SAM
|
||||
* method in the functional interface. The type can be different when parameterized types are used. For example,
|
||||
* consider
|
||||
* <code>interface I<T> { int m(T x); }</code> if this SAM type is used in a lambda
|
||||
* <code>I<Byte> v = ...</code>, we need both the actual SAM method which has the signature
|
||||
* <code>(Object)int</code> and the functional interface type of the method, which has signature
|
||||
* <code>(Byte)int</code>. The latter is the instantiated erased functional interface method type, or
|
||||
* simply <I>instantiated method type</I>.
|
||||
* <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
|
||||
* <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
|
||||
@ -138,11 +137,25 @@ package java.lang.invoke;
|
||||
* </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
|
||||
* or additional marker superinterfaces.
|
||||
*
|
||||
*/
|
||||
public class LambdaMetafactory {
|
||||
|
||||
/** 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
|
||||
* besides Serializable
|
||||
*/
|
||||
public static final int FLAG_MARKERS = 1 << 1;
|
||||
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -158,7 +171,8 @@ public class LambdaMetafactory {
|
||||
* @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 SAM method from the functional interface's perspective
|
||||
* @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
|
||||
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
||||
@ -171,7 +185,85 @@ public class LambdaMetafactory {
|
||||
MethodType instantiatedMethodType)
|
||||
throws ReflectiveOperationException, LambdaConversionException {
|
||||
AbstractValidatingLambdaMetafactory mf;
|
||||
mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
|
||||
mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType,
|
||||
0, EMPTY_CLASS_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.
|
||||
*
|
||||
* The declared argument list for this method is:
|
||||
*
|
||||
* 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,
|
||||
* String invokedName,
|
||||
* MethodType invokedType,
|
||||
* MethodHandle samMethod
|
||||
* MethodHandle implMethod,
|
||||
* MethodType instantiatedMethodType,
|
||||
* int flags,
|
||||
* int markerInterfaceCount, // IF flags has MARKERS set
|
||||
* Class... markerInterfaces // IF flags has MARKERS 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 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 and FLAG_MARKERS.
|
||||
* @param markerInterfaceCount If the FLAG_MARKERS flag is set, this is a count of the number of additional
|
||||
* marker interfaces
|
||||
* @param markerInterfaces If the FLAG_MARKERS flag is set, this consists of Class objects identifying additional
|
||||
* marker interfaces which the lambda object should implement, whose count equals
|
||||
* markerInterfaceCount
|
||||
* @return a CallSite, which, when invoked, will return an instance of the functional interface
|
||||
* @throws ReflectiveOperationException
|
||||
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
||||
*/
|
||||
public static CallSite altMetaFactory(MethodHandles.Lookup caller,
|
||||
String invokedName,
|
||||
MethodType invokedType,
|
||||
Object... args)
|
||||
throws ReflectiveOperationException, LambdaConversionException {
|
||||
MethodHandle samMethod = (MethodHandle)args[0];
|
||||
MethodHandle implMethod = (MethodHandle)args[1];
|
||||
MethodType instantiatedMethodType = (MethodType)args[2];
|
||||
int flags = (Integer) args[3];
|
||||
Class<?>[] markerInterfaces;
|
||||
int argIndex = 4;
|
||||
if ((flags & FLAG_MARKERS) != 0) {
|
||||
int markerCount = (Integer) args[argIndex++];
|
||||
markerInterfaces = new Class<?>[markerCount];
|
||||
System.arraycopy(args, argIndex, markerInterfaces, 0, markerCount);
|
||||
argIndex += markerCount;
|
||||
}
|
||||
else
|
||||
markerInterfaces = EMPTY_CLASS_ARRAY;
|
||||
AbstractValidatingLambdaMetafactory mf;
|
||||
mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType,
|
||||
flags, markerInterfaces);
|
||||
mf.validateMetafactoryArgs();
|
||||
return mf.buildCallSite();
|
||||
}
|
||||
|
@ -26,8 +26,11 @@
|
||||
package java.lang.invoke;
|
||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
||||
|
||||
//Not yet public: public
|
||||
class MethodHandleInfo {
|
||||
/**
|
||||
* Cracking (reflecting) method handles back into their constituent symbolic parts.
|
||||
*
|
||||
*/
|
||||
final class MethodHandleInfo {
|
||||
public static final int
|
||||
REF_NONE = Constants.REF_NONE,
|
||||
REF_getField = Constants.REF_getField,
|
||||
@ -65,7 +68,33 @@ class MethodHandleInfo {
|
||||
return methodType;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return -1; //TODO
|
||||
}
|
||||
|
||||
public int getReferenceKind() {
|
||||
return referenceKind;
|
||||
}
|
||||
|
||||
static String getReferenceKindString(int referenceKind) {
|
||||
switch (referenceKind) {
|
||||
case REF_NONE: return "REF_NONE";
|
||||
case REF_getField: return "getfield";
|
||||
case REF_getStatic: return "getstatic";
|
||||
case REF_putField: return "putfield";
|
||||
case REF_putStatic: return "putstatic";
|
||||
case REF_invokeVirtual: return "invokevirtual";
|
||||
case REF_invokeStatic: return "invokestatic";
|
||||
case REF_invokeSpecial: return "invokespecial";
|
||||
case REF_newInvokeSpecial: return "newinvokespecial";
|
||||
case REF_invokeInterface: return "invokeinterface";
|
||||
default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
|
||||
declaringClass.getName(), name, methodType);
|
||||
}
|
||||
}
|
||||
|
209
jdk/src/share/classes/java/lang/invoke/SerializedLambda.java
Normal file
209
jdk/src/share/classes/java/lang/invoke/SerializedLambda.java
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Serialized form of a lambda expression. The properties of this class represent the information that is present
|
||||
* at the lambda factory site, including the identity of the primary functional interface method, the identity of the
|
||||
* implementation method, and any variables captured from the local environment at the time of lambda capture.
|
||||
*
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
public final class SerializedLambda implements Serializable {
|
||||
private static final long serialVersionUID = 8025925345765570181L;
|
||||
private final String capturingClass;
|
||||
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;
|
||||
private final int implMethodKind;
|
||||
private final String instantiatedMethodType;
|
||||
private final Object[] capturedArgs;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* lambda factory site
|
||||
* @param functionalInterfaceMethodSignature Signature of the primary method for the functional interface 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 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
|
||||
* the lambda
|
||||
*/
|
||||
public SerializedLambda(String capturingClass,
|
||||
int functionalInterfaceMethodKind,
|
||||
String functionalInterfaceClass,
|
||||
String functionalInterfaceMethodName,
|
||||
String functionalInterfaceMethodSignature,
|
||||
int implMethodKind,
|
||||
String implClass,
|
||||
String implMethodName,
|
||||
String implMethodSignature,
|
||||
String instantiatedMethodType,
|
||||
Object[] capturedArgs) {
|
||||
this.capturingClass = capturingClass;
|
||||
this.functionalInterfaceMethodKind = functionalInterfaceMethodKind;
|
||||
this.functionalInterfaceClass = functionalInterfaceClass;
|
||||
this.functionalInterfaceMethodName = functionalInterfaceMethodName;
|
||||
this.functionalInterfaceMethodSignature = functionalInterfaceMethodSignature;
|
||||
this.implMethodKind = implMethodKind;
|
||||
this.implClass = implClass;
|
||||
this.implMethodName = implMethodName;
|
||||
this.implMethodSignature = implMethodSignature;
|
||||
this.instantiatedMethodType = instantiatedMethodType;
|
||||
this.capturedArgs = Objects.requireNonNull(capturedArgs).clone();
|
||||
}
|
||||
|
||||
/** Get the name of the class that captured this lambda */
|
||||
public String getCapturingClass() {
|
||||
return capturingClass;
|
||||
}
|
||||
|
||||
/** Get the name of the functional interface class to which this lambda has been converted */
|
||||
public String getFunctionalInterfaceClass() {
|
||||
return functionalInterfaceClass;
|
||||
}
|
||||
|
||||
/** Get the name of the primary method for the functional interface to which this lambda has been converted */
|
||||
public String getFunctionalInterfaceMethodName() {
|
||||
return functionalInterfaceMethodName;
|
||||
}
|
||||
|
||||
/** Get the signature of the primary method for the functional interface to which this lambda has been converted */
|
||||
public String getFunctionalInterfaceMethodSignature() {
|
||||
return functionalInterfaceMethodSignature;
|
||||
}
|
||||
|
||||
/** Get the method handle kind (see {@link MethodHandleInfo}) of the primary method for the functional interface
|
||||
* to which this lambda has been converted */
|
||||
public int getFunctionalInterfaceMethodKind() {
|
||||
return functionalInterfaceMethodKind;
|
||||
}
|
||||
|
||||
/** Get the name of the class containing the implementation method */
|
||||
public String getImplClass() {
|
||||
return implClass;
|
||||
}
|
||||
|
||||
/** Get the name of the implementation method */
|
||||
public String getImplMethodName() {
|
||||
return implMethodName;
|
||||
}
|
||||
|
||||
/** Get the signature of the implementation method */
|
||||
public String getImplMethodSignature() {
|
||||
return implMethodSignature;
|
||||
}
|
||||
|
||||
/** Get the method handle kind (see {@link MethodHandleInfo}) of the implementation method */
|
||||
public int getImplMethodKind() {
|
||||
return implMethodKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature of the primary functional interface method after type variables are substituted with
|
||||
* their instantiation from the capture site
|
||||
*/
|
||||
public final String getInstantiatedMethodType() {
|
||||
return instantiatedMethodType;
|
||||
}
|
||||
|
||||
/** Get the count of dynamic arguments to the lambda capture site */
|
||||
public int getCapturedArgCount() {
|
||||
return capturedArgs.length;
|
||||
}
|
||||
|
||||
/** Get a dynamic argument to the lambda capture site */
|
||||
public Object getCapturedArg(int i) {
|
||||
return capturedArgs[i];
|
||||
}
|
||||
|
||||
private Object readResolve() throws ReflectiveOperationException {
|
||||
try {
|
||||
Method deserialize = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
|
||||
@Override
|
||||
public Method run() throws Exception {
|
||||
Class<?> clazz = Class.forName(capturingClass.replace('/', '.'), true,
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
Method m = clazz.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return deserialize.invoke(null, this);
|
||||
}
|
||||
catch (PrivilegedActionException e) {
|
||||
Exception cause = e.getException();
|
||||
if (cause instanceof ReflectiveOperationException)
|
||||
throw (ReflectiveOperationException) cause;
|
||||
else if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException) cause;
|
||||
else
|
||||
throw new RuntimeException("Exception in SerializedLambda.readResolve", e);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
/*
|
||||
// @@@ Review question: is it worthwhile implementing a versioned serialization protocol?
|
||||
|
||||
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
|
||||
}
|
||||
|
||||
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
|
||||
}
|
||||
*/
|
||||
}
|
@ -27,6 +27,7 @@ package java.lang.invoke;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import static sun.invoke.util.Wrapper.*;
|
||||
|
||||
@ -49,6 +50,9 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
|
||||
private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
|
||||
|
||||
// Table of wrappers for primitives, indexed by ASM type sorts
|
||||
private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16];
|
||||
|
||||
static {
|
||||
for (Wrapper w : Wrapper.values()) {
|
||||
if (w.basicTypeChar() != 'L') {
|
||||
@ -71,6 +75,15 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
|
||||
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
|
||||
initWidening(DOUBLE, Opcodes.L2D, LONG);
|
||||
|
||||
FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE;
|
||||
FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT;
|
||||
FROM_TYPE_SORT[Type.INT] = Wrapper.INT;
|
||||
FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG;
|
||||
FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR;
|
||||
FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT;
|
||||
FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE;
|
||||
FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN;
|
||||
}
|
||||
|
||||
private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
|
||||
@ -124,8 +137,9 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
return "()" + w.basicTypeChar();
|
||||
}
|
||||
|
||||
void boxIfPrimitive(Wrapper w) {
|
||||
if (w.zero() != null) {
|
||||
void boxIfTypePrimitive(Type t) {
|
||||
Wrapper w = FROM_TYPE_SORT[t.getSort()];
|
||||
if (w != null) {
|
||||
box(w);
|
||||
}
|
||||
}
|
||||
@ -264,4 +278,22 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following method is copied from
|
||||
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
|
||||
* and fast Java bytecode manipulation framework.
|
||||
* Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
|
||||
*/
|
||||
void iconst(final int cst) {
|
||||
if (cst >= -1 && cst <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, cst);
|
||||
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, cst);
|
||||
} else {
|
||||
mv.visitLdcInsn(cst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
jdk/test/java/lang/invoke/lambda/LambdaSerialization.java
Normal file
84
jdk/test/java/lang/invoke/lambda/LambdaSerialization.java
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
@bug 8004970
|
||||
@summary Lambda serialization
|
||||
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class LambdaSerialization {
|
||||
|
||||
static int assertionCount = 0;
|
||||
|
||||
static void assertTrue(boolean cond) {
|
||||
assertionCount++;
|
||||
if (!cond)
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
// Write lambdas out
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutput out = new ObjectOutputStream(baos);
|
||||
|
||||
write(out, z -> "[" + z + "]" );
|
||||
write(out, z -> z + z );
|
||||
write(out, z -> "blah" );
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
// Read them back
|
||||
ByteArrayInputStream bais =
|
||||
new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
readAssert(in, "[X]");
|
||||
readAssert(in, "XX");
|
||||
readAssert(in, "blah");
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
assertTrue(assertionCount == 3);
|
||||
}
|
||||
|
||||
static void write(ObjectOutput out, LSI lamb) throws IOException {
|
||||
out.writeObject(lamb);
|
||||
}
|
||||
|
||||
static void readAssert(ObjectInputStream in, String expected) throws IOException, ClassNotFoundException {
|
||||
LSI ls = (LSI) in.readObject();
|
||||
String result = ls.convert("X");
|
||||
System.out.printf("Result: %s\n", result);
|
||||
assertTrue(result.equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
interface LSI extends Serializable {
|
||||
String convert(String x);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user