提交 e8b256a6 编写于 作者: R rfield

8000806: Implement runtime lambda metafactory

Summary: Implement lambda invokedynamic bootstrap by generating at runtime an inner class that implements the functional interface
Reviewed-by: twisti
上级 9bccbe27
/*
* 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;
}
}
}
/*
* 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);
}
}
}
/*
* 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);
}
}
/*
* 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&lt;T&gt; { int m(T x); }</code> if this SAM type is used in a lambda
* <code>I&lt;Byte&gt; 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();
}
}
/*
* 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 {
}
/*
* 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);
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册