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.*;
|
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:
|
* 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 Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
|
||||||
final MethodType invokedType; // The type of the invoked method "(CC)II"
|
final MethodType invokedType; // The type of the invoked method "(CC)II"
|
||||||
final Class<?> samBase; // The type of the returned instance "interface JJ"
|
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 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 Class<?> samClass; // Interface containing the SAM method "interface II"
|
||||||
final MethodType samMethodType; // Type of the SAM method "(Object)Object"
|
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 MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]"
|
||||||
final int implKind; // Invocation kind for implementation "5"=invokevirtual
|
final int implKind; // Invocation kind for implementation "5"=invokevirtual
|
||||||
final boolean implIsInstanceMethod; // Is the implementation an instance method "true"
|
final boolean implIsInstanceMethod; // Is the implementation an instance method "true"
|
||||||
final Class<?> implDefiningClass; // Type defining the implementation "class CC"
|
final Class<?> implDefiningClass; // Type defining the implementation "class CC"
|
||||||
final MethodType implMethodType; // Type of the implementation method "(int)String"
|
final MethodType implMethodType; // Type of the implementation method "(int)String"
|
||||||
final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object"
|
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
|
* @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
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
* functional interface instance are invoked.
|
* 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 ReflectiveOperationException
|
||||||
|
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
||||||
*/
|
*/
|
||||||
AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
|
AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
|
||||||
MethodType invokedType,
|
MethodType invokedType,
|
||||||
MethodHandle samMethod,
|
MethodHandle samMethod,
|
||||||
MethodHandle implMethod,
|
MethodHandle implMethod,
|
||||||
MethodType instantiatedMethodType)
|
MethodType instantiatedMethodType,
|
||||||
throws ReflectiveOperationException {
|
int flags,
|
||||||
|
Class<?>[] markerInterfaces)
|
||||||
|
throws ReflectiveOperationException, LambdaConversionException {
|
||||||
this.targetClass = caller.lookupClass();
|
this.targetClass = caller.lookupClass();
|
||||||
this.invokedType = invokedType;
|
this.invokedType = invokedType;
|
||||||
|
|
||||||
this.samBase = invokedType.returnType();
|
this.samBase = invokedType.returnType();
|
||||||
this.isSerializable = Serializable.class.isAssignableFrom(samBase);
|
|
||||||
|
|
||||||
|
this.samMethod = samMethod;
|
||||||
this.samInfo = new MethodHandleInfo(samMethod);
|
this.samInfo = new MethodHandleInfo(samMethod);
|
||||||
this.samClass = samInfo.getDeclaringClass();
|
this.samClass = samInfo.getDeclaringClass();
|
||||||
this.samMethodType = samInfo.getMethodType();
|
this.samMethodType = samInfo.getMethodType();
|
||||||
|
|
||||||
|
this.implMethod = implMethod;
|
||||||
this.implInfo = new MethodHandleInfo(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 =
|
this.implIsInstanceMethod =
|
||||||
implKind == MethodHandleInfo.REF_invokeVirtual ||
|
implKind == MethodHandleInfo.REF_invokeVirtual ||
|
||||||
implKind == MethodHandleInfo.REF_invokeSpecial ||
|
implKind == MethodHandleInfo.REF_invokeSpecial ||
|
||||||
@ -109,6 +120,30 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
this.implMethodType = implInfo.getMethodType();
|
this.implMethodType = implInfo.getMethodType();
|
||||||
|
|
||||||
this.instantiatedMethodType = instantiatedMethodType;
|
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,7 +162,8 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
void validateMetafactoryArgs() throws LambdaConversionException {
|
void validateMetafactoryArgs() throws LambdaConversionException {
|
||||||
// Check target type is a subtype of class where SAM method is defined
|
// Check target type is a subtype of class where SAM method is defined
|
||||||
if (!samClass.isAssignableFrom(samBase)) {
|
if (!samClass.isAssignableFrom(samBase)) {
|
||||||
throw new LambdaConversionException(String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s",
|
throw new LambdaConversionException(
|
||||||
|
String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s",
|
||||||
samBase.getName(), samClass.getName()));
|
samBase.getName(), samClass.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +185,14 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
final int samArity = samMethodType.parameterCount();
|
final int samArity = samMethodType.parameterCount();
|
||||||
final int instantiatedArity = instantiatedMethodType.parameterCount();
|
final int instantiatedArity = instantiatedMethodType.parameterCount();
|
||||||
if (implArity + receiverArity != capturedArity + samArity) {
|
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",
|
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,
|
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||||
capturedArity, samArity, implArity));
|
capturedArity, samArity, implArity));
|
||||||
}
|
}
|
||||||
if (instantiatedArity != samArity) {
|
if (instantiatedArity != samArity) {
|
||||||
throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d functional interface parameters, %d SAM method parameters",
|
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,
|
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||||
instantiatedArity, samArity));
|
instantiatedArity, samArity));
|
||||||
}
|
}
|
||||||
@ -180,7 +218,8 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
|
|
||||||
// check receiver type
|
// check receiver type
|
||||||
if (!implDefiningClass.isAssignableFrom(receiverClass)) {
|
if (!implDefiningClass.isAssignableFrom(receiverClass)) {
|
||||||
throw new LambdaConversionException(String.format("Invalid receiver type %s; not a subtype of implementation type %s",
|
throw new LambdaConversionException(
|
||||||
|
String.format("Invalid receiver type %s; not a subtype of implementation type %s",
|
||||||
receiverClass, implDefiningClass));
|
receiverClass, implDefiningClass));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -196,7 +235,8 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
|
Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
|
||||||
if (!capturedParamType.equals(implParamType)) {
|
if (!capturedParamType.equals(implParamType)) {
|
||||||
throw new LambdaConversionException(
|
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
|
// Check for adaptation match on SAM arguments
|
||||||
@ -206,7 +246,8 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
|
Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
|
||||||
if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
|
if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
|
||||||
throw new LambdaConversionException(
|
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();
|
: implMethodType.returnType();
|
||||||
if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
|
if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
|
||||||
throw new LambdaConversionException(
|
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
|
/*********** Logging support -- for debugging only, uncomment as needed
|
||||||
static final Executor logPool = Executors.newSingleThreadExecutor(); // @@@ For debugging only
|
static final Executor logPool = Executors.newSingleThreadExecutor();
|
||||||
protected static void log(final String s) {
|
protected static void log(final String s) {
|
||||||
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||||
@Override
|
@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
|
* Find the functional interface method and corresponding abstract methods
|
||||||
* will have the same name and number of parameters. Check for matching default methods (non-abstract), they
|
* which should be bridged. The functional interface method and those to be
|
||||||
* should not be bridged-over and indicate a complex bridging situation.
|
* 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 {
|
class MethodAnalyzer {
|
||||||
private final Method[] methods = samBase.getMethods();
|
private final Method[] methods = samBase.getMethods();
|
||||||
private final List<Method> methodsFound = new ArrayList<>(methods.length);
|
|
||||||
|
|
||||||
private Method samMethod = null;
|
private Method samMethod = null;
|
||||||
private final List<Method> methodsToBridge = new ArrayList<>(methods.length);
|
private final List<Method> methodsToBridge = new ArrayList<>(methods.length);
|
||||||
private boolean defaultMethodFound = false;
|
private boolean conflictFoundBetweenDefaultAndBridge = false;
|
||||||
|
|
||||||
MethodAnalyzer() {
|
MethodAnalyzer() {
|
||||||
String samMethodName = samInfo.getName();
|
String samMethodName = samInfo.getName();
|
||||||
@ -315,31 +361,36 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
int samParamLength = samParamTypes.length;
|
int samParamLength = samParamTypes.length;
|
||||||
Class<?> samReturnType = samMethodType.returnType();
|
Class<?> samReturnType = samMethodType.returnType();
|
||||||
Class<?> objectClass = Object.class;
|
Class<?> objectClass = Object.class;
|
||||||
|
List<Method> defaultMethods = new ArrayList<>(methods.length);
|
||||||
|
|
||||||
for (Method m : methods) {
|
for (Method m : methods) {
|
||||||
if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) {
|
if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) {
|
||||||
Class<?>[] mParamTypes = m.getParameterTypes();
|
Class<?>[] mParamTypes = m.getParameterTypes();
|
||||||
if (mParamTypes.length == samParamLength) {
|
if (mParamTypes.length == samParamLength) {
|
||||||
|
// Method matches name and parameter length -- and is not Object
|
||||||
if (Modifier.isAbstract(m.getModifiers())) {
|
if (Modifier.isAbstract(m.getModifiers())) {
|
||||||
// Exclude methods with duplicate signatures
|
// Method is abstract
|
||||||
if (methodUnique(m)) {
|
if (m.getReturnType().equals(samReturnType)
|
||||||
if (m.getReturnType().equals(samReturnType) && Arrays.equals(mParamTypes, samParamTypes)) {
|
&& Arrays.equals(mParamTypes, samParamTypes)) {
|
||||||
// Exact match, this is the SAM method signature
|
// Exact match, this is the SAM method signature
|
||||||
samMethod = m;
|
samMethod = m;
|
||||||
} else {
|
} else if (!hasMatchingBridgeSignature(m)) {
|
||||||
|
// Record bridges, exclude methods with duplicate signatures
|
||||||
methodsToBridge.add(m);
|
methodsToBridge.add(m);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// This is a default method, flag for special processing
|
// Record default methods for conflict testing
|
||||||
defaultMethodFound = true;
|
defaultMethods.add(m);
|
||||||
// Ignore future matching abstracts.
|
|
||||||
// Note, due to reabstraction, this is really a punt, hence pass-off to VM
|
|
||||||
methodUnique(m);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (Method dm : defaultMethods) {
|
||||||
|
if (hasMatchingBridgeSignature(dm)) {
|
||||||
|
conflictFoundBetweenDefaultAndBridge = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Method getSamMethod() {
|
Method getSamMethod() {
|
||||||
@ -350,27 +401,26 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
return methodsToBridge;
|
return methodsToBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean wasDefaultMethodFound() {
|
boolean conflictFoundBetweenDefaultAndBridge() {
|
||||||
return defaultMethodFound;
|
return conflictFoundBetweenDefaultAndBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search the list of previously found methods to determine if there is a method with the same signature
|
* 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. If it wasn't found before, add to the found list.
|
* (return and parameter types) as the specified method.
|
||||||
*
|
*
|
||||||
* @param m The method to match
|
* @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<?>[] ptypes = m.getParameterTypes();
|
||||||
Class<?> rtype = m.getReturnType();
|
Class<?> rtype = m.getReturnType();
|
||||||
for (Method md : methodsFound) {
|
for (Method md : methodsToBridge) {
|
||||||
if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
|
if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methodsFound.add(m);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,21 +36,28 @@ import java.security.AccessController;
|
|||||||
import java.security.PrivilegedAction;
|
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 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 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_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
|
||||||
private static final String NAME_SERIALIZABLE = "java/io/Serializable";
|
|
||||||
private static final String NAME_CTOR = "<init>";
|
private static final String NAME_CTOR = "<init>";
|
||||||
|
|
||||||
//Serialization support
|
//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 DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
||||||
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
||||||
private static final String NAME_OBJECT = "java/lang/Object";
|
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
|
// Used to ensure that each spun class name is unique
|
||||||
private static final AtomicInteger counter = new AtomicInteger(0);
|
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
|
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
|
* @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
|
||||||
* of the caller.
|
* 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
|
* @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
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
* functional interface instance are invoked.
|
* 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 ReflectiveOperationException
|
||||||
|
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
||||||
*/
|
*/
|
||||||
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
||||||
MethodType invokedType,
|
MethodType invokedType,
|
||||||
MethodHandle samMethod,
|
MethodHandle samMethod,
|
||||||
MethodHandle implMethod,
|
MethodHandle implMethod,
|
||||||
MethodType instantiatedMethodType)
|
MethodType instantiatedMethodType,
|
||||||
throws ReflectiveOperationException {
|
int flags,
|
||||||
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
|
Class<?>[] markerInterfaces)
|
||||||
|
throws ReflectiveOperationException, LambdaConversionException {
|
||||||
|
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces);
|
||||||
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
||||||
implMethodName = implInfo.getName();
|
implMethodName = implInfo.getName();
|
||||||
implMethodDesc = implMethodType.toMethodDescriptorString();
|
implMethodDesc = implMethodType.toMethodDescriptorString();
|
||||||
@ -109,7 +123,6 @@ import java.security.PrivilegedAction;
|
|||||||
argNames[i] = "arg$" + (i + 1);
|
argNames[i] = "arg$" + (i + 1);
|
||||||
}
|
}
|
||||||
instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
|
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
|
* @return a CallSite, which, when invoked, will return an instance of the
|
||||||
* functional interface
|
* functional interface
|
||||||
* @throws ReflectiveOperationException, LambdaConversionException
|
* @throws ReflectiveOperationException
|
||||||
|
* @throws LambdaConversionException If properly formed functional interface is not found
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
|
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
|
||||||
@ -161,16 +175,23 @@ import java.security.PrivilegedAction;
|
|||||||
* interface, define and return the class.
|
* interface, define and return the class.
|
||||||
*
|
*
|
||||||
* @return a Class which implements the functional interface
|
* @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('.', '/');
|
String samName = samBase.getName().replace('.', '/');
|
||||||
|
String[] interfaces = new String[markerInterfaces.length + 1];
|
||||||
cw.visit(CLASSFILE_VERSION, ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
|
interfaces[0] = samName;
|
||||||
isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{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
|
// Generate final fields to be filled in by constructor
|
||||||
for (int i = 0; i < argTypes.length; i++) {
|
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();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,26 +201,24 @@ import java.security.PrivilegedAction;
|
|||||||
|
|
||||||
// Forward the SAM method
|
// Forward the SAM method
|
||||||
if (ma.getSamMethod() == null) {
|
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 {
|
} else {
|
||||||
generateForwardingMethod(ma.getSamMethod(), false);
|
generateForwardingMethod(ma.getSamMethod(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward the bridges
|
// Forward the bridges
|
||||||
// @@@ Once the VM can do fail-over, uncomment the default method test
|
// @@@ The commented-out code is temporary, pending the VM's ability to bridge all methods on request
|
||||||
if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) {
|
// @@@ 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()) {
|
for (Method m : ma.getMethodsToBridge()) {
|
||||||
generateForwardingMethod(m, true);
|
generateForwardingMethod(m, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***** Serialization not yet supported
|
|
||||||
if (isSerializable) {
|
if (isSerializable) {
|
||||||
String samMethodName = samInfo.getName();
|
generateWriteReplace();
|
||||||
Type samType = Type.getType(samBase);
|
|
||||||
generateSerializationMethod(samType, samMethodName);
|
|
||||||
}
|
}
|
||||||
******/
|
|
||||||
|
|
||||||
cw.visitEnd();
|
cw.visitEnd();
|
||||||
|
|
||||||
@ -212,7 +231,7 @@ import java.security.PrivilegedAction;
|
|||||||
try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
|
try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
|
||||||
fos.write(classBytes);
|
fos.write(classBytes);
|
||||||
} catch (IOException ex) {
|
} 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 generateWriteReplace() {
|
||||||
private void generateSerializationMethod(Type samType, String samMethodName) {
|
TypeConvertingMethodAdapter mv
|
||||||
String samMethodDesc = samMethodType.toMethodDescriptorString();
|
= new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
||||||
TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null));
|
NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
|
||||||
|
null, null));
|
||||||
|
|
||||||
mv.visitCode();
|
mv.visitCode();
|
||||||
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
|
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
|
||||||
mv.dup();
|
mv.visitInsn(DUP);;
|
||||||
mv.visitLdcInsn(samType);
|
mv.visitLdcInsn(targetClass.getName().replace('.', '/'));
|
||||||
mv.visitLdcInsn(samMethodName);
|
mv.visitLdcInsn(samInfo.getReferenceKind());
|
||||||
mv.visitLdcInsn(samMethodDesc);
|
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
|
||||||
mv.visitLdcInsn(Type.getType(implDefiningClass));
|
mv.visitLdcInsn(samInfo.getName());
|
||||||
mv.visitLdcInsn(implMethodName);
|
mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString());
|
||||||
mv.visitLdcInsn(implMethodDesc);
|
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.iconst(argTypes.length);
|
||||||
mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
|
mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
|
||||||
for (int i = 0; i < argTypes.length; i++) {
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
mv.dup();
|
mv.visitInsn(DUP);
|
||||||
mv.iconst(i);
|
mv.iconst(i);
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||||
mv.boxIfPrimitive(argTypes[i]);
|
mv.boxIfTypePrimitive(argTypes[i]);
|
||||||
mv.visitInsn(AASTORE);
|
mv.visitInsn(AASTORE);
|
||||||
}
|
}
|
||||||
mv.invokespecial(NAME_SERIALIZED_LAMBDA, NAME_CTOR,
|
mv.visitMethodInsn(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");
|
DESCR_CTOR_SERIALIZED_LAMBDA);
|
||||||
mv.visitInsn(ARETURN);
|
mv.visitInsn(ARETURN);
|
||||||
mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||||
mv.visitEnd();
|
mv.visitEnd();
|
||||||
}
|
}
|
||||||
********/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a method which calls the lambda implementation method,
|
* Generate a method which calls the lambda implementation method,
|
||||||
@ -321,11 +345,11 @@ import java.security.PrivilegedAction;
|
|||||||
|
|
||||||
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
||||||
visitTypeInsn(NEW, implMethodClassName);
|
visitTypeInsn(NEW, implMethodClassName);
|
||||||
dup();
|
visitInsn(DUP);;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < argTypes.length; i++) {
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
visitVarInsn(ALOAD, 0);
|
visitVarInsn(ALOAD, 0);
|
||||||
getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
convertArgumentTypes(Type.getArgumentTypes(m));
|
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
|
// Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
|
||||||
Type samReturnType = Type.getReturnType(m);
|
Type samReturnType = Type.getReturnType(m);
|
||||||
convertType(implMethodReturnType, samReturnType, samReturnType);
|
convertType(implMethodReturnType, samReturnType, samReturnType);
|
||||||
areturn(samReturnType);
|
visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
|
||||||
|
|
||||||
visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||||
visitEnd();
|
visitEnd();
|
||||||
@ -352,7 +376,7 @@ import java.security.PrivilegedAction;
|
|||||||
Type rcvrType = samArgumentTypes[0];
|
Type rcvrType = samArgumentTypes[0];
|
||||||
Type instantiatedRcvrType = instantiatedArgumentTypes[0];
|
Type instantiatedRcvrType = instantiatedArgumentTypes[0];
|
||||||
|
|
||||||
load(lvIndex + 1, rcvrType);
|
visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
|
||||||
lvIndex += rcvrType.getSize();
|
lvIndex += rcvrType.getSize();
|
||||||
convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
|
convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
|
||||||
}
|
}
|
||||||
@ -362,7 +386,7 @@ import java.security.PrivilegedAction;
|
|||||||
Type targetType = implMethodArgumentTypes[argOffset + i];
|
Type targetType = implMethodArgumentTypes[argOffset + i];
|
||||||
Type instantiatedArgType = instantiatedArgumentTypes[i];
|
Type instantiatedArgType = instantiatedArgumentTypes[i];
|
||||||
|
|
||||||
load(lvIndex + 1, argType);
|
visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
|
||||||
lvIndex += argType.getSize();
|
lvIndex += argType.getSize();
|
||||||
convertType(argType, targetType, instantiatedArgType);
|
convertType(argType, targetType, instantiatedArgType);
|
||||||
}
|
}
|
||||||
@ -388,45 +412,5 @@ import java.security.PrivilegedAction;
|
|||||||
throw new InternalError("Unexpected invocation kind: " + implKind);
|
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,
|
* method, and the static types of the captured lambda arguments, and link a call site which, when invoked,
|
||||||
* produces the lambda object.
|
* produces the lambda object.
|
||||||
*
|
*
|
||||||
* <p>Two pieces of information are needed about the functional interface: the SAM method and the type of the SAM
|
* <p>When parameterized types are used, the instantiated type of the functional interface method may be different
|
||||||
* method in the functional interface. The type can be different when parameterized types are used. For example,
|
* from that in the functional interface. For example, consider
|
||||||
* consider
|
* <code>interface I<T> { int m(T x); }</code> if this functional interface type is used in a lambda
|
||||||
* <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 functional interface method which has the signature
|
||||||
* <code>I<Byte> v = ...</code>, we need both the actual SAM method which has the signature
|
* <code>(Object)int</code> and the erased instantiated type of the functional interface method (or simply
|
||||||
* <code>(Object)int</code> and the functional interface type of the method, which has signature
|
* <I>instantiated method type</I>), which has signature
|
||||||
* <code>(Byte)int</code>. The latter is the instantiated erased functional interface method type, or
|
* <code>(Byte)int</code>.
|
||||||
* simply <I>instantiated method type</I>.
|
|
||||||
*
|
*
|
||||||
* <p>While functional interfaces only have a single abstract method from the language perspective (concrete
|
* <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 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>
|
* </tr>
|
||||||
* </table>
|
* </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 {
|
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.
|
* 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
|
* @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
|
* @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
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
* functional interface instance are invoked.
|
* 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
|
* @return a CallSite, which, when invoked, will return an instance of the functional interface
|
||||||
* @throws ReflectiveOperationException
|
* @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
|
||||||
@ -171,7 +185,85 @@ public class LambdaMetafactory {
|
|||||||
MethodType instantiatedMethodType)
|
MethodType instantiatedMethodType)
|
||||||
throws ReflectiveOperationException, LambdaConversionException {
|
throws ReflectiveOperationException, LambdaConversionException {
|
||||||
AbstractValidatingLambdaMetafactory mf;
|
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();
|
mf.validateMetafactoryArgs();
|
||||||
return mf.buildCallSite();
|
return mf.buildCallSite();
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,11 @@
|
|||||||
package java.lang.invoke;
|
package java.lang.invoke;
|
||||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
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
|
public static final int
|
||||||
REF_NONE = Constants.REF_NONE,
|
REF_NONE = Constants.REF_NONE,
|
||||||
REF_getField = Constants.REF_getField,
|
REF_getField = Constants.REF_getField,
|
||||||
@ -65,7 +68,33 @@ class MethodHandleInfo {
|
|||||||
return methodType;
|
return methodType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getModifiers() {
|
||||||
|
return -1; //TODO
|
||||||
|
}
|
||||||
|
|
||||||
public int getReferenceKind() {
|
public int getReferenceKind() {
|
||||||
return referenceKind;
|
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.MethodVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
|
import jdk.internal.org.objectweb.asm.Type;
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
import static 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];
|
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 {
|
static {
|
||||||
for (Wrapper w : Wrapper.values()) {
|
for (Wrapper w : Wrapper.values()) {
|
||||||
if (w.basicTypeChar() != 'L') {
|
if (w.basicTypeChar() != 'L') {
|
||||||
@ -71,6 +75,15 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
|||||||
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
|
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
|
||||||
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
|
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
|
||||||
initWidening(DOUBLE, Opcodes.L2D, LONG);
|
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) {
|
private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
|
||||||
@ -124,8 +137,9 @@ class TypeConvertingMethodAdapter extends MethodVisitor {
|
|||||||
return "()" + w.basicTypeChar();
|
return "()" + w.basicTypeChar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void boxIfPrimitive(Wrapper w) {
|
void boxIfTypePrimitive(Type t) {
|
||||||
if (w.zero() != null) {
|
Wrapper w = FROM_TYPE_SORT[t.getSort()];
|
||||||
|
if (w != null) {
|
||||||
box(w);
|
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