8000806: Implement runtime lambda metafactory
Implement lambda invokedynamic bootstrap by generating at runtime an inner class that implements the functional interface Reviewed-by: twisti
This commit is contained in:
parent
78aeafc1a2
commit
020472d122
@ -0,0 +1,376 @@
|
|||||||
|
/*
|
||||||
|
* 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.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import sun.invoke.util.Wrapper;
|
||||||
|
import static sun.invoke.util.Wrapper.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract implementation of a meta-factory which provides parameter unrolling and input validation.
|
||||||
|
*
|
||||||
|
* @author Robert Field
|
||||||
|
*/
|
||||||
|
/*non-public*/ abstract class AbstractValidatingLambdaMetafactory {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For context, the comments for the following fields are marked in quotes with their values, given this program:
|
||||||
|
* interface II<T> { Object foo(T x); }
|
||||||
|
* interface JJ<R extends Number> extends II<R> { }
|
||||||
|
* class CC { String impl(int i) { return "impl:"+i; }}
|
||||||
|
* class X {
|
||||||
|
* public static void main(String[] args) {
|
||||||
|
* JJ<Integer> iii = (new CC())::impl;
|
||||||
|
* System.out.printf(">>> %s\n", iii.foo(44));
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
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 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 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"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta-factory constructor.
|
||||||
|
*
|
||||||
|
* @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
|
||||||
|
* of the caller.
|
||||||
|
* @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
|
||||||
|
* expected static type of the returned lambda object, and the static types of the captured
|
||||||
|
* arguments for the lambda. In the event that the implementation method is an instance method,
|
||||||
|
* the first argument in the invocation signature will correspond to the receiver.
|
||||||
|
* @param samMethod The primary method in the functional interface to which the lambda or method reference is
|
||||||
|
* being converted, represented as a method handle.
|
||||||
|
* @param implMethod The implementation method which should be called (with suitable adaptation of argument
|
||||||
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
|
* functional interface instance are invoked.
|
||||||
|
* @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
|
||||||
|
* @throws ReflectiveOperationException
|
||||||
|
*/
|
||||||
|
AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
|
||||||
|
MethodType invokedType,
|
||||||
|
MethodHandle samMethod,
|
||||||
|
MethodHandle implMethod,
|
||||||
|
MethodType instantiatedMethodType)
|
||||||
|
throws ReflectiveOperationException {
|
||||||
|
this.targetClass = caller.lookupClass();
|
||||||
|
this.invokedType = invokedType;
|
||||||
|
|
||||||
|
this.samBase = invokedType.returnType();
|
||||||
|
this.isSerializable = Serializable.class.isAssignableFrom(samBase);
|
||||||
|
|
||||||
|
this.samInfo = new MethodHandleInfo(samMethod);
|
||||||
|
this.samClass = samInfo.getDeclaringClass();
|
||||||
|
this.samMethodType = samInfo.getMethodType();
|
||||||
|
|
||||||
|
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
|
||||||
|
this.implIsInstanceMethod =
|
||||||
|
implKind == MethodHandleInfo.REF_invokeVirtual ||
|
||||||
|
implKind == MethodHandleInfo.REF_invokeSpecial ||
|
||||||
|
implKind == MethodHandleInfo.REF_invokeInterface;
|
||||||
|
this.implDefiningClass = implInfo.getDeclaringClass();
|
||||||
|
this.implMethodType = implInfo.getMethodType();
|
||||||
|
|
||||||
|
this.instantiatedMethodType = instantiatedMethodType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the CallSite.
|
||||||
|
*
|
||||||
|
* @return a CallSite, which, when invoked, will return an instance of the
|
||||||
|
* functional interface
|
||||||
|
* @throws ReflectiveOperationException
|
||||||
|
*/
|
||||||
|
abstract CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the meta-factory arguments for errors
|
||||||
|
* @throws LambdaConversionException if there are improper conversions
|
||||||
|
*/
|
||||||
|
void validateMetafactoryArgs() throws LambdaConversionException {
|
||||||
|
// Check target type is a subtype of class where SAM method is defined
|
||||||
|
if (!samClass.isAssignableFrom(samBase)) {
|
||||||
|
throw new LambdaConversionException(String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s",
|
||||||
|
samBase.getName(), samClass.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (implKind) {
|
||||||
|
case MethodHandleInfo.REF_invokeInterface:
|
||||||
|
case MethodHandleInfo.REF_invokeVirtual:
|
||||||
|
case MethodHandleInfo.REF_invokeStatic:
|
||||||
|
case MethodHandleInfo.REF_newInvokeSpecial:
|
||||||
|
case MethodHandleInfo.REF_invokeSpecial:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new LambdaConversionException(String.format("Unsupported MethodHandle kind: %s", implInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check arity: optional-receiver + captured + SAM == impl
|
||||||
|
final int implArity = implMethodType.parameterCount();
|
||||||
|
final int receiverArity = implIsInstanceMethod ? 1 : 0;
|
||||||
|
final int capturedArity = invokedType.parameterCount();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If instance: first captured arg (receiver) must be subtype of class where impl method is defined
|
||||||
|
final int capturedStart;
|
||||||
|
final int samStart;
|
||||||
|
if (implIsInstanceMethod) {
|
||||||
|
final Class<?> receiverClass;
|
||||||
|
|
||||||
|
// implementation is an instance method, adjust for receiver in captured variables / SAM arguments
|
||||||
|
if (capturedArity == 0) {
|
||||||
|
// receiver is function parameter
|
||||||
|
capturedStart = 0;
|
||||||
|
samStart = 1;
|
||||||
|
receiverClass = instantiatedMethodType.parameterType(0);
|
||||||
|
} else {
|
||||||
|
// receiver is a captured variable
|
||||||
|
capturedStart = 1;
|
||||||
|
samStart = 0;
|
||||||
|
receiverClass = invokedType.parameterType(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no receiver
|
||||||
|
capturedStart = 0;
|
||||||
|
samStart = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for exact match on non-receiver captured arguments
|
||||||
|
final int implFromCaptured = capturedArity - capturedStart;
|
||||||
|
for (int i=0; i<implFromCaptured; i++) {
|
||||||
|
Class<?> implParamType = implMethodType.parameterType(i);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for adaptation match on SAM arguments
|
||||||
|
final int samOffset = samStart - implFromCaptured;
|
||||||
|
for (int i=implFromCaptured; i<implArity; i++) {
|
||||||
|
Class<?> implParamType = implMethodType.parameterType(i);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adaptation match: return type
|
||||||
|
Class<?> expectedType = instantiatedMethodType.returnType();
|
||||||
|
Class<?> actualReturnType =
|
||||||
|
(implKind == MethodHandleInfo.REF_newInvokeSpecial)
|
||||||
|
? implDefiningClass
|
||||||
|
: implMethodType.returnType();
|
||||||
|
if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
|
||||||
|
throw new LambdaConversionException(
|
||||||
|
String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check type adaptability
|
||||||
|
* @param fromType
|
||||||
|
* @param toType
|
||||||
|
* @param strict If true, do strict checks, else allow that fromType may be parameterized
|
||||||
|
* @return True if 'fromType' can be passed to an argument of 'toType'
|
||||||
|
*/
|
||||||
|
private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
|
||||||
|
if (fromType.equals(toType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (fromType.isPrimitive()) {
|
||||||
|
Wrapper wfrom = forPrimitiveType(fromType);
|
||||||
|
if (toType.isPrimitive()) {
|
||||||
|
// both are primitive: widening
|
||||||
|
Wrapper wto = forPrimitiveType(toType);
|
||||||
|
return wto.isConvertibleFrom(wfrom);
|
||||||
|
} else {
|
||||||
|
// from primitive to reference: boxing
|
||||||
|
return toType.isAssignableFrom(wfrom.wrapperType());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (toType.isPrimitive()) {
|
||||||
|
// from reference to primitive: unboxing
|
||||||
|
Wrapper wfrom;
|
||||||
|
if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
|
||||||
|
// fromType is a primitive wrapper; unbox+widen
|
||||||
|
Wrapper wto = forPrimitiveType(toType);
|
||||||
|
return wto.isConvertibleFrom(wfrom);
|
||||||
|
} else {
|
||||||
|
// must be convertible to primitive
|
||||||
|
return !strict;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// both are reference types: fromType should be a superclass of toType.
|
||||||
|
return strict? toType.isAssignableFrom(fromType) : true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check type adaptability for return types -- special handling of void type) and parameterized fromType
|
||||||
|
* @param fromType
|
||||||
|
* @param toType
|
||||||
|
* @return True if 'fromType' can be converted to 'toType'
|
||||||
|
*/
|
||||||
|
private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
|
||||||
|
return toType.equals(void.class)
|
||||||
|
|| !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********** Logging support -- for debugging only
|
||||||
|
static final Executor logPool = Executors.newSingleThreadExecutor(); // @@@ For debugging only
|
||||||
|
protected static void log(final String s) {
|
||||||
|
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void log(final String s, final Throwable e) {
|
||||||
|
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
System.out.println(s);
|
||||||
|
e.printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
***********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
MethodAnalyzer() {
|
||||||
|
String samMethodName = samInfo.getName();
|
||||||
|
Class<?>[] samParamTypes = samMethodType.parameterArray();
|
||||||
|
int samParamLength = samParamTypes.length;
|
||||||
|
Class<?> samReturnType = samMethodType.returnType();
|
||||||
|
Class<?> objectClass = Object.class;
|
||||||
|
|
||||||
|
for (Method m : methods) {
|
||||||
|
if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) {
|
||||||
|
Class<?>[] mParamTypes = m.getParameterTypes();
|
||||||
|
if (mParamTypes.length == samParamLength) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Method getSamMethod() {
|
||||||
|
return samMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Method> getMethodsToBridge() {
|
||||||
|
return methodsToBridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wasDefaultMethodFound() {
|
||||||
|
return defaultMethodFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param m The method to match
|
||||||
|
* @return False if the method was found, True otherwise
|
||||||
|
*/
|
||||||
|
private boolean methodUnique(Method m) {
|
||||||
|
Class<?>[] ptypes = m.getParameterTypes();
|
||||||
|
Class<?> rtype = m.getReturnType();
|
||||||
|
for (Method md : methodsFound) {
|
||||||
|
if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methodsFound.add(m);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* 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.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InnerClassLambdaMetafactory
|
||||||
|
*/
|
||||||
|
/*non-public*/ 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 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";
|
||||||
|
|
||||||
|
// Used to ensure that each spun class name is unique
|
||||||
|
private static final AtomicInteger counter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// See context values in AbstractValidatingLambdaMetafactory
|
||||||
|
private final String implMethodClassName; // Name of type containing implementation "CC"
|
||||||
|
private final String implMethodName; // Name of implementation method "impl"
|
||||||
|
private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
|
||||||
|
private final Type[] implMethodArgumentTypes; // ASM types for implementaion method parameters
|
||||||
|
private final Type implMethodReturnType; // ASM type for implementaion method return type "Ljava/lang/String;"
|
||||||
|
private final MethodType constructorType; // Generated class constructor type "(CC)void"
|
||||||
|
private final String constructorDesc; // Type descriptor for constructor "(LCC;)V"
|
||||||
|
private final ClassWriter cw; // ASM class writer
|
||||||
|
private final Type[] argTypes; // ASM types for the constructor arguments
|
||||||
|
private final String[] argNames; // Generated names for the constructor arguments
|
||||||
|
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
|
||||||
|
private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta-factory constructor.
|
||||||
|
*
|
||||||
|
* @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
|
||||||
|
* of the caller.
|
||||||
|
* @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
|
||||||
|
* expected static type of the returned lambda object, and the static types of the captured
|
||||||
|
* arguments for the lambda. In the event that the implementation method is an instance method,
|
||||||
|
* the first argument in the invocation signature will correspond to the receiver.
|
||||||
|
* @param samMethod The primary method in the functional interface to which the lambda or method reference is
|
||||||
|
* being converted, represented as a method handle.
|
||||||
|
* @param implMethod The implementation method which should be called (with suitable adaptation of argument
|
||||||
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
|
* functional interface instance are invoked.
|
||||||
|
* @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
|
||||||
|
* @throws ReflectiveOperationException
|
||||||
|
*/
|
||||||
|
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
||||||
|
MethodType invokedType,
|
||||||
|
MethodHandle samMethod,
|
||||||
|
MethodHandle implMethod,
|
||||||
|
MethodType instantiatedMethodType)
|
||||||
|
throws ReflectiveOperationException {
|
||||||
|
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
|
||||||
|
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
||||||
|
implMethodName = implInfo.getName();
|
||||||
|
implMethodDesc = implMethodType.toMethodDescriptorString();
|
||||||
|
Type implMethodAsmType = Type.getMethodType(implMethodDesc);
|
||||||
|
implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
|
||||||
|
implMethodReturnType = implMethodAsmType.getReturnType();
|
||||||
|
constructorType = invokedType.changeReturnType(Void.TYPE);
|
||||||
|
constructorDesc = constructorType.toMethodDescriptorString();
|
||||||
|
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||||
|
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||||
|
argTypes = Type.getArgumentTypes(constructorDesc);
|
||||||
|
argNames = new String[argTypes.length];
|
||||||
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
|
argNames[i] = "arg$" + (i + 1);
|
||||||
|
}
|
||||||
|
instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the CallSite. Generate a class file which implements the functional
|
||||||
|
* interface, define the class, if there are no parameters create an instance
|
||||||
|
* of the class which the CallSite will return, otherwise, generate handles
|
||||||
|
* which will call the class' constructor.
|
||||||
|
*
|
||||||
|
* @return a CallSite, which, when invoked, will return an instance of the
|
||||||
|
* functional interface
|
||||||
|
* @throws ReflectiveOperationException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
|
||||||
|
final Class<?> innerClass = spinInnerClass();
|
||||||
|
if (invokedType.parameterCount() == 0) {
|
||||||
|
return new ConstantCallSite(MethodHandles.constant(samBase, innerClass.newInstance()));
|
||||||
|
} else {
|
||||||
|
return new ConstantCallSite(
|
||||||
|
MethodHandles.Lookup.IMPL_LOOKUP
|
||||||
|
.findConstructor(innerClass, constructorType)
|
||||||
|
.asType(constructorType.changeReturnType(samBase)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a class file which implements the functional
|
||||||
|
* interface, define and return the class.
|
||||||
|
*
|
||||||
|
* @return a Class which implements the functional interface
|
||||||
|
*/
|
||||||
|
private <T> Class<? extends T> spinInnerClass() throws LambdaConversionException {
|
||||||
|
String samName = samBase.getName().replace('.', '/');
|
||||||
|
|
||||||
|
cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
|
||||||
|
isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateConstructor();
|
||||||
|
|
||||||
|
MethodAnalyzer ma = new MethodAnalyzer();
|
||||||
|
|
||||||
|
// Forward the SAM method
|
||||||
|
if (ma.getSamMethod() == null) {
|
||||||
|
throw new LambdaConversionException(String.format("SAM 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() */) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
******/
|
||||||
|
|
||||||
|
cw.visitEnd();
|
||||||
|
|
||||||
|
// Define the generated class in this VM.
|
||||||
|
|
||||||
|
final byte[] classBytes = cw.toByteArray();
|
||||||
|
|
||||||
|
if (System.getProperty("debug.dump.generated") != null) {
|
||||||
|
System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
|
||||||
|
fos.write(classBytes);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(InnerClassLambdaMetafactory.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLoader loader = targetClass.getClassLoader();
|
||||||
|
ProtectionDomain pd = (loader == null) ? null : targetClass.getProtectionDomain();
|
||||||
|
return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the constructor for the class
|
||||||
|
*/
|
||||||
|
private void generateConstructor() {
|
||||||
|
// Generate constructor
|
||||||
|
MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null);
|
||||||
|
ctor.visitCode();
|
||||||
|
ctor.visitVarInsn(ALOAD, 0);
|
||||||
|
ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, METHOD_DESCRIPTOR_VOID);
|
||||||
|
int lvIndex = 0;
|
||||||
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
|
ctor.visitVarInsn(ALOAD, 0);
|
||||||
|
ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
|
||||||
|
lvIndex += argTypes[i].getSize();
|
||||||
|
ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||||
|
}
|
||||||
|
ctor.visitInsn(RETURN);
|
||||||
|
ctor.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||||
|
ctor.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the serialization method (if needed)
|
||||||
|
*/
|
||||||
|
/****** 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));
|
||||||
|
|
||||||
|
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.iconst(argTypes.length);
|
||||||
|
mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
|
||||||
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
|
mv.dup();
|
||||||
|
mv.iconst(i);
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||||
|
mv.boxIfPrimitive(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.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,
|
||||||
|
* converting arguments, as needed.
|
||||||
|
* @param m The method whose signature should be generated
|
||||||
|
* @param isBridge True if this methods should be flagged as a bridge
|
||||||
|
*/
|
||||||
|
private void generateForwardingMethod(Method m, boolean isBridge) {
|
||||||
|
Class<?>[] exceptionTypes = m.getExceptionTypes();
|
||||||
|
String[] exceptionNames = new String[exceptionTypes.length];
|
||||||
|
for (int i = 0; i < exceptionTypes.length; i++) {
|
||||||
|
exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
|
||||||
|
}
|
||||||
|
String methodDescriptor = Type.getMethodDescriptor(m);
|
||||||
|
int access = isBridge? ACC_PUBLIC | ACC_BRIDGE : ACC_PUBLIC;
|
||||||
|
MethodVisitor mv = cw.visitMethod(access, m.getName(), methodDescriptor, null, exceptionNames);
|
||||||
|
new ForwardingMethodGenerator(mv).generate(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class generates a method body which calls the lambda implementation
|
||||||
|
* method, converting arguments, as needed.
|
||||||
|
*/
|
||||||
|
private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
|
||||||
|
|
||||||
|
ForwardingMethodGenerator(MethodVisitor mv) {
|
||||||
|
super(mv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate(Method m) throws InternalError {
|
||||||
|
visitCode();
|
||||||
|
|
||||||
|
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
||||||
|
visitTypeInsn(NEW, implMethodClassName);
|
||||||
|
dup();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < argTypes.length; i++) {
|
||||||
|
visitVarInsn(ALOAD, 0);
|
||||||
|
getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
convertArgumentTypes(Type.getArgumentTypes(m));
|
||||||
|
|
||||||
|
// Invoke the method we want to forward to
|
||||||
|
visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
|
||||||
|
|
||||||
|
// Convert the return value (if any) and return it
|
||||||
|
// Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
|
||||||
|
Type samReturnType = Type.getReturnType(m);
|
||||||
|
convertType(implMethodReturnType, samReturnType, samReturnType);
|
||||||
|
areturn(samReturnType);
|
||||||
|
|
||||||
|
visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||||
|
visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertArgumentTypes(Type[] samArgumentTypes) {
|
||||||
|
int lvIndex = 0;
|
||||||
|
boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0;
|
||||||
|
int samReceiverLength = samIncludesReceiver ? 1 : 0;
|
||||||
|
if (samIncludesReceiver) {
|
||||||
|
// push receiver
|
||||||
|
Type rcvrType = samArgumentTypes[0];
|
||||||
|
Type instantiatedRcvrType = instantiatedArgumentTypes[0];
|
||||||
|
|
||||||
|
load(lvIndex + 1, rcvrType);
|
||||||
|
lvIndex += rcvrType.getSize();
|
||||||
|
convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
|
||||||
|
}
|
||||||
|
int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length;
|
||||||
|
for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
|
||||||
|
Type argType = samArgumentTypes[i];
|
||||||
|
Type targetType = implMethodArgumentTypes[argOffset + i];
|
||||||
|
Type instantiatedArgType = instantiatedArgumentTypes[i];
|
||||||
|
|
||||||
|
load(lvIndex + 1, argType);
|
||||||
|
lvIndex += argType.getSize();
|
||||||
|
convertType(argType, targetType, instantiatedArgType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertType(Type argType, Type targetType, Type functionalType) {
|
||||||
|
convertType(argType.getDescriptor(), targetType.getDescriptor(), functionalType.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int invocationOpcode() throws InternalError {
|
||||||
|
switch (implKind) {
|
||||||
|
case MethodHandleInfo.REF_invokeStatic:
|
||||||
|
return INVOKESTATIC;
|
||||||
|
case MethodHandleInfo.REF_newInvokeSpecial:
|
||||||
|
return INVOKESPECIAL;
|
||||||
|
case MethodHandleInfo.REF_invokeVirtual:
|
||||||
|
return INVOKEVIRTUAL;
|
||||||
|
case MethodHandleInfo.REF_invokeInterface:
|
||||||
|
return INVOKEINTERFACE;
|
||||||
|
case MethodHandleInfo.REF_invokeSpecial:
|
||||||
|
return INVOKESPECIAL;
|
||||||
|
default:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LambdaConversionException
|
||||||
|
*/
|
||||||
|
public class LambdaConversionException extends Exception {
|
||||||
|
public LambdaConversionException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LambdaConversionException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LambdaConversionException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LambdaConversionException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LambdaConversionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
178
jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java
Normal file
178
jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Bootstrap methods for converting lambda expressions and method references to functional interface objects.</p>
|
||||||
|
*
|
||||||
|
* <p>For every lambda expressions or method reference in the source code, there is a target type which is a
|
||||||
|
* functional interface. Evaluating a lambda expression produces an object of its target type. The mechanism for
|
||||||
|
* evaluating lambda expressions is to invoke an invokedynamic call site, which takes arguments describing the sole
|
||||||
|
* method of the functional interface and the implementation method, and returns an object (the lambda object) that
|
||||||
|
* implements the target type. Methods of the lambda object invoke the implementation method. For method
|
||||||
|
* references, the implementation method is simply the referenced method; for lambda expressions, the
|
||||||
|
* implementation method is produced by the compiler based on the body of the lambda expression. The methods in
|
||||||
|
* this file are the bootstrap methods for those invokedynamic call sites, called lambda factories, and the
|
||||||
|
* bootstrap methods responsible for linking the lambda factories are called lambda meta-factories.
|
||||||
|
*
|
||||||
|
* <p>The bootstrap methods in this class take the information about the functional interface, the implementation
|
||||||
|
* 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>While functional interfaces only have a single abstract method from the language perspective (concrete
|
||||||
|
* methods in Object are and default methods may be present), at the bytecode level they may actually have multiple
|
||||||
|
* methods because of the need for bridge methods. Invoking any of these methods on the lambda object will result
|
||||||
|
* in invoking the implementation method.
|
||||||
|
*
|
||||||
|
* <p>The argument list of the implementation method and the argument list of the functional interface method(s)
|
||||||
|
* may differ in several ways. The implementation methods may have additional arguments to accommodate arguments
|
||||||
|
* captured by the lambda expression; there may also be differences resulting from permitted adaptations of
|
||||||
|
* arguments, such as casting, boxing, unboxing, and primitive widening. They may also differ because of var-args,
|
||||||
|
* but this is expected to be handled by the compiler.
|
||||||
|
*
|
||||||
|
* <p>Invokedynamic call sites have two argument lists: a static argument list and a dynamic argument list. The
|
||||||
|
* static argument list lives in the constant pool; the dynamic argument list lives on the operand stack at
|
||||||
|
* invocation time. The bootstrap method has access to the entire static argument list (which in this case,
|
||||||
|
* contains method handles describing the implementation method and the canonical functional interface method),
|
||||||
|
* as well as a method signature describing the number and static types (but not the values) of the dynamic
|
||||||
|
* arguments, and the static return type of the invokedynamic site.
|
||||||
|
*
|
||||||
|
* <p>The implementation method is described with a method handle. In theory, any method handle could be used.
|
||||||
|
* Currently supported are method handles representing invocation of virtual, interface, constructor and static
|
||||||
|
* methods.
|
||||||
|
*
|
||||||
|
* <p>Assume:
|
||||||
|
* <ul>
|
||||||
|
* <li>the functional interface method has N arguments, of types (U1, U2, ... Un) and return type Ru</li>
|
||||||
|
* <li>then the instantiated method type also has N arguments, of types (T1, T2, ... Tn) and return type Rt</li>
|
||||||
|
* <li>the implementation method has M arguments, of types (A1..Am) and return type Ra,</li>
|
||||||
|
* <li>the dynamic argument list has K arguments of types (D1..Dk), and the invokedynamic return site has
|
||||||
|
* type Rd</li>
|
||||||
|
* <li>the functional interface type is F</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The following signature invariants must hold:
|
||||||
|
* <ul>
|
||||||
|
* <li>Rd is a subtype of F</li>
|
||||||
|
* <li>For i=1..N, Ti is a subtype of Ui</li>
|
||||||
|
* <li>Either Rt and Ru are primitive and are the same type, or both are reference types and
|
||||||
|
* Rt is a subtype of Ru</li>
|
||||||
|
* <li>If the implementation method is a static method:
|
||||||
|
* <ul>
|
||||||
|
* <li>K + N = M</li>
|
||||||
|
* <li>For i=1..K, Di = Ai</li>
|
||||||
|
* <li>For i=1..N, Ti is adaptable to Aj, where j=i+k</li>
|
||||||
|
* </ul></li>
|
||||||
|
* <li>If the implementation method is an instance method:
|
||||||
|
* <ul>
|
||||||
|
* <li>K + N = M + 1</li>
|
||||||
|
* <li>D1 must be a subtype of the enclosing class for the implementation method</li>
|
||||||
|
* <li>For i=2..K, Di = Aj, where j=i-1</li>
|
||||||
|
* <li>For i=1..N, Ti is adaptable to Aj, where j=i+k-1</li>
|
||||||
|
* </ul></li>
|
||||||
|
* <li>The return type Rt is void, or the return type Ra is not void and is adaptable to Rt</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Note that the potentially parameterized implementation return type provides the value for the SAM. Whereas
|
||||||
|
* the completely known instantiated return type is adapted to the implementation arguments. Because the
|
||||||
|
* instantiated type of the implementation method is not available, the adaptability of return types cannot be
|
||||||
|
* checked as precisely at link-time as the arguments can be checked. Thus a loose version of link-time checking is
|
||||||
|
* done on return type, while a strict version is applied to arguments.
|
||||||
|
*
|
||||||
|
* <p>A type Q is considered adaptable to S as follows:
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Q</th><th>S</th><th>Link-time checks</th><th>Capture-time checks</th></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Primitive</td><td>Primitive</td>
|
||||||
|
* <td>Q can be converted to S via a primitive widening conversion</td>
|
||||||
|
* <td>None</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Primitive</td><td>Reference</td>
|
||||||
|
* <td>S is a supertype of the Wrapper(Q)</td>
|
||||||
|
* <td>Cast from Wrapper(Q) to S</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Reference</td><td>Primitive</td>
|
||||||
|
* <td>strict: Q is a primitive wrapper and Primitive(Q) can be widened to S
|
||||||
|
* <br>loose: If Q is a primitive wrapper, check that Primitive(Q) can be widened to S</td>
|
||||||
|
* <td>If Q is not a primitive wrapper, cast Q to the base Wrapper(S); for example Number for numeric types</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Reference</td><td>Reference</td>
|
||||||
|
* <td>strict: S is a supertype of Q
|
||||||
|
* <br>loose: none</td>
|
||||||
|
* <td>Cast from Q to S</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LambdaMetafactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard meta-factory for conversion of lambda expressions or method references to functional interfaces.
|
||||||
|
*
|
||||||
|
* @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
|
||||||
|
* of the caller.
|
||||||
|
* @param invokedName Stacked automatically by VM; the name of the invoked method as it appears at the call site.
|
||||||
|
* Currently unused.
|
||||||
|
* @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
|
||||||
|
* expected static type of the returned lambda object, and the static types of the captured
|
||||||
|
* arguments for the lambda. In the event that the implementation method is an instance method,
|
||||||
|
* the first argument in the invocation signature will correspond to the receiver.
|
||||||
|
* @param samMethod The primary method in the functional interface to which the lambda or method reference is
|
||||||
|
* being converted, represented as a method handle.
|
||||||
|
* @param implMethod The implementation method which should be called (with suitable adaptation of argument
|
||||||
|
* types, return types, and adjustment for captured arguments) when methods of the resulting
|
||||||
|
* functional interface instance are invoked.
|
||||||
|
* @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
|
||||||
|
* @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 metaFactory(MethodHandles.Lookup caller,
|
||||||
|
String invokedName,
|
||||||
|
MethodType invokedType,
|
||||||
|
MethodHandle samMethod,
|
||||||
|
MethodHandle implMethod,
|
||||||
|
MethodType instantiatedMethodType)
|
||||||
|
throws ReflectiveOperationException, LambdaConversionException {
|
||||||
|
AbstractValidatingLambdaMetafactory mf;
|
||||||
|
mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
|
||||||
|
mf.validateMetafactoryArgs();
|
||||||
|
return mf.buildCallSite();
|
||||||
|
}
|
||||||
|
}
|
39
jdk/src/share/classes/java/lang/invoke/MagicLambdaImpl.java
Normal file
39
jdk/src/share/classes/java/lang/invoke/MagicLambdaImpl.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/** <P> MagicLambdaImpl (named for similarity to MagicAccessorImpl and
|
||||||
|
others, not because it actually implements an interface) is a
|
||||||
|
marker class in the hierarchy. All subclasses of this class are
|
||||||
|
"magically" granted access by the VM to otherwise inaccessible
|
||||||
|
fields and methods of other classes. It is distinct from MagicAccessorImpl
|
||||||
|
because, while we want to bypass accessibility checks, we do not want to
|
||||||
|
bypass verification.</P>
|
||||||
|
|
||||||
|
<P> Do not change the name of this class without also changing the
|
||||||
|
VM's code. </P> */
|
||||||
|
|
||||||
|
class MagicLambdaImpl {
|
||||||
|
}
|
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* 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 jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
|
import sun.invoke.util.Wrapper;
|
||||||
|
import static sun.invoke.util.Wrapper.*;
|
||||||
|
|
||||||
|
class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||||
|
|
||||||
|
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
||||||
|
super(Opcodes.ASM4, mv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int NUM_WRAPPERS = Wrapper.values().length;
|
||||||
|
|
||||||
|
private static final String NAME_OBJECT = "java/lang/Object";
|
||||||
|
private static final String WRAPPER_PREFIX = "Ljava/lang/";
|
||||||
|
|
||||||
|
// Same for all primitives; name of the boxing method
|
||||||
|
private static final String NAME_BOX_METHOD = "valueOf";
|
||||||
|
|
||||||
|
// Table of opcodes for widening primitive conversions; NOP = no conversion
|
||||||
|
private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
|
||||||
|
|
||||||
|
private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (Wrapper w : Wrapper.values()) {
|
||||||
|
if (w.basicTypeChar() != 'L') {
|
||||||
|
int wi = hashWrapperName(w.wrapperSimpleName());
|
||||||
|
assert (FROM_WRAPPER_NAME[wi] == null);
|
||||||
|
FROM_WRAPPER_NAME[wi] = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_WRAPPERS; i++) {
|
||||||
|
for (int j = 0; j < NUM_WRAPPERS; j++) {
|
||||||
|
wideningOpcodes[i][j] = Opcodes.NOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR);
|
||||||
|
initWidening(LONG, Opcodes.F2L, FLOAT);
|
||||||
|
initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR);
|
||||||
|
initWidening(FLOAT, Opcodes.L2F, LONG);
|
||||||
|
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
|
||||||
|
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
|
||||||
|
initWidening(DOUBLE, Opcodes.L2D, LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
|
||||||
|
for (Wrapper f : from) {
|
||||||
|
wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class name to Wrapper hash, derived from Wrapper.hashWrap()
|
||||||
|
* @param xn
|
||||||
|
* @return The hash code 0-15
|
||||||
|
*/
|
||||||
|
private static int hashWrapperName(String xn) {
|
||||||
|
if (xn.length() < 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Wrapper wrapperOrNullFromDescriptor(String desc) {
|
||||||
|
if (!desc.startsWith(WRAPPER_PREFIX)) {
|
||||||
|
// Not a class type (array or method), so not a boxed type
|
||||||
|
// or not in the right package
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Pare it down to the simple class name
|
||||||
|
String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
|
||||||
|
// Hash to a Wrapper
|
||||||
|
Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
|
||||||
|
if (w == null || w.wrapperSimpleName().equals(cname)) {
|
||||||
|
return w;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String wrapperName(Wrapper w) {
|
||||||
|
return "java/lang/" + w.wrapperSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unboxMethod(Wrapper w) {
|
||||||
|
return w.primitiveSimpleName() + "Value";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String boxingDescriptor(Wrapper w) {
|
||||||
|
return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unboxingDescriptor(Wrapper w) {
|
||||||
|
return "()" + w.basicTypeChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void boxIfPrimitive(Wrapper w) {
|
||||||
|
if (w.zero() != null) {
|
||||||
|
box(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void widen(Wrapper ws, Wrapper wt) {
|
||||||
|
if (ws != wt) {
|
||||||
|
int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
|
||||||
|
if (opcode != Opcodes.NOP) {
|
||||||
|
visitInsn(opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void box(Wrapper w) {
|
||||||
|
visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||||
|
wrapperName(w),
|
||||||
|
NAME_BOX_METHOD,
|
||||||
|
boxingDescriptor(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert types by unboxing. The source type is known to be a primitive wrapper.
|
||||||
|
* @param ws A primitive wrapper corresponding to wrapped reference source type
|
||||||
|
* @param wt A primitive wrapper being converted to
|
||||||
|
*/
|
||||||
|
void unbox(String sname, Wrapper wt) {
|
||||||
|
visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||||
|
sname,
|
||||||
|
unboxMethod(wt),
|
||||||
|
unboxingDescriptor(wt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String descriptorToName(String desc) {
|
||||||
|
int last = desc.length() - 1;
|
||||||
|
if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
|
||||||
|
// In descriptor form
|
||||||
|
return desc.substring(1, last);
|
||||||
|
} else {
|
||||||
|
// Already in internal name form
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cast(String ds, String dt) {
|
||||||
|
String ns = descriptorToName(ds);
|
||||||
|
String nt = descriptorToName(dt);
|
||||||
|
if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
|
||||||
|
visitTypeInsn(Opcodes.CHECKCAST, nt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPrimitive(Wrapper w) {
|
||||||
|
return w != OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Wrapper toWrapper(String desc) {
|
||||||
|
char first = desc.charAt(0);
|
||||||
|
if (first == '[' || first == '(') {
|
||||||
|
first = 'L';
|
||||||
|
}
|
||||||
|
return Wrapper.forBasicType(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an argument of type 'argType' to be passed to 'targetType' assuring that it is 'functionalType'.
|
||||||
|
* Insert the needed conversion instructions in the method code.
|
||||||
|
* @param argType
|
||||||
|
* @param targetType
|
||||||
|
* @param functionalType
|
||||||
|
*/
|
||||||
|
void convertType(String dArg, String dTarget, String dFunctional) {
|
||||||
|
if (dArg.equals(dTarget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Wrapper wArg = toWrapper(dArg);
|
||||||
|
Wrapper wTarget = toWrapper(dTarget);
|
||||||
|
if (wArg == VOID || wTarget == VOID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isPrimitive(wArg)) {
|
||||||
|
if (isPrimitive(wTarget)) {
|
||||||
|
// Both primitives: widening
|
||||||
|
widen(wArg, wTarget);
|
||||||
|
} else {
|
||||||
|
// Primitive argument to reference target
|
||||||
|
Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
|
||||||
|
if (wPrimTarget != null) {
|
||||||
|
// The target is a boxed primitive type, widen to get there before boxing
|
||||||
|
widen(wArg, wPrimTarget);
|
||||||
|
box(wPrimTarget);
|
||||||
|
} else {
|
||||||
|
// Otherwise, box and cast
|
||||||
|
box(wArg);
|
||||||
|
cast(wrapperName(wArg), dTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String dSrc;
|
||||||
|
Wrapper wFunctional = toWrapper(dFunctional);
|
||||||
|
if (isPrimitive(wFunctional)) {
|
||||||
|
dSrc = dArg;
|
||||||
|
} else {
|
||||||
|
// Cast to convert to possibly more specific type, and generate CCE for invalid arg
|
||||||
|
dSrc = dFunctional;
|
||||||
|
cast(dArg, dFunctional);
|
||||||
|
}
|
||||||
|
if (isPrimitive(wTarget)) {
|
||||||
|
// Reference argument to primitive target
|
||||||
|
Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
|
||||||
|
if (wps != null) {
|
||||||
|
if (wps.isSigned() || wps.isFloating()) {
|
||||||
|
// Boxed number to primitive
|
||||||
|
unbox(wrapperName(wps), wTarget);
|
||||||
|
} else {
|
||||||
|
// Character or Boolean
|
||||||
|
unbox(wrapperName(wps), wps);
|
||||||
|
widen(wps, wTarget);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Source type is reference type, but not boxed type,
|
||||||
|
// assume it is super type of target type
|
||||||
|
String intermediate;
|
||||||
|
if (wTarget.isSigned() || wTarget.isFloating()) {
|
||||||
|
// Boxed number to primitive
|
||||||
|
intermediate = "java/lang/Number";
|
||||||
|
} else {
|
||||||
|
// Character or Boolean
|
||||||
|
intermediate = wrapperName(wTarget);
|
||||||
|
}
|
||||||
|
cast(dSrc, intermediate);
|
||||||
|
unbox(intermediate, wTarget);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Both reference types: just case to target type
|
||||||
|
cast(dSrc, dTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user