2012-10-25 17:34:24 -07:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2012-12-06 21:55:55 -08:00
|
|
|
import java.lang.reflect.Constructor;
|
2012-10-25 17:34:24 -07:00
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.security.ProtectionDomain;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import jdk.internal.org.objectweb.asm.*;
|
|
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|
|
|
import sun.misc.Unsafe;
|
2012-12-06 21:55:55 -08:00
|
|
|
import java.security.AccessController;
|
|
|
|
import java.security.PrivilegedAction;
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
/**
|
2013-02-16 12:36:54 -08:00
|
|
|
* Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite.
|
|
|
|
*
|
|
|
|
* @see LambdaMetafactory
|
2012-10-25 17:34:24 -07:00
|
|
|
*/
|
2013-02-16 12:36:54 -08:00
|
|
|
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
2012-10-25 17:34:24 -07:00
|
|
|
private static final int CLASSFILE_VERSION = 51;
|
|
|
|
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
|
|
|
private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
|
|
|
|
private static final String NAME_CTOR = "<init>";
|
|
|
|
|
|
|
|
//Serialization support
|
2013-02-16 12:36:54 -08:00
|
|
|
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
2012-10-25 17:34:24 -07:00
|
|
|
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";
|
2013-02-16 12:36:54 -08:00
|
|
|
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
|
|
|
= MethodType.methodType(void.class,
|
2013-02-26 10:38:58 -08:00
|
|
|
Class.class,
|
2013-02-16 12:36:54 -08:00
|
|
|
int.class, String.class, String.class, String.class,
|
|
|
|
int.class, String.class, String.class, String.class,
|
|
|
|
String.class,
|
|
|
|
Object[].class).toMethodDescriptorString();
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
/**
|
2013-02-16 12:36:54 -08:00
|
|
|
* General meta-factory constructor, standard cases and allowing for uncommon options such as serialization.
|
2012-10-25 17:34:24 -07:00
|
|
|
*
|
|
|
|
* @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.
|
2013-02-16 12:36:54 -08:00
|
|
|
* @param instantiatedMethodType The signature of the primary functional interface method after type variables
|
|
|
|
* are substituted with their instantiation from the capture site
|
|
|
|
* @param flags A bitmask containing flags that may influence the translation of this lambda expression. Defined
|
|
|
|
* fields include FLAG_SERIALIZABLE.
|
|
|
|
* @param markerInterfaces Additional interfaces which the lambda object should implement.
|
2012-10-25 17:34:24 -07:00
|
|
|
* @throws ReflectiveOperationException
|
2013-02-16 12:36:54 -08:00
|
|
|
* @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
|
2012-10-25 17:34:24 -07:00
|
|
|
*/
|
|
|
|
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
|
|
|
MethodType invokedType,
|
|
|
|
MethodHandle samMethod,
|
|
|
|
MethodHandle implMethod,
|
2013-02-16 12:36:54 -08:00
|
|
|
MethodType instantiatedMethodType,
|
|
|
|
int flags,
|
|
|
|
Class<?>[] markerInterfaces)
|
|
|
|
throws ReflectiveOperationException, LambdaConversionException {
|
|
|
|
super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces);
|
2012-10-25 17:34:24 -07:00
|
|
|
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
|
2013-02-16 12:36:54 -08:00
|
|
|
* @throws ReflectiveOperationException
|
|
|
|
* @throws LambdaConversionException If properly formed functional interface is not found
|
2012-10-25 17:34:24 -07:00
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
|
|
|
|
final Class<?> innerClass = spinInnerClass();
|
|
|
|
if (invokedType.parameterCount() == 0) {
|
2012-12-06 21:55:55 -08:00
|
|
|
final Constructor[] ctrs = AccessController.doPrivileged(
|
|
|
|
new PrivilegedAction<Constructor[]>() {
|
|
|
|
@Override
|
|
|
|
public Constructor[] run() {
|
|
|
|
return innerClass.getDeclaredConstructors();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (ctrs.length != 1) {
|
|
|
|
throw new ReflectiveOperationException("Expected one lambda constructor for "
|
|
|
|
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
|
|
|
|
}
|
|
|
|
// The lambda implementing inner class constructor is private, set
|
|
|
|
// it accessible (by us) before creating the constant sole instance
|
|
|
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
|
|
|
@Override
|
|
|
|
public Void run() {
|
|
|
|
ctrs[0].setAccessible(true);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Object inst = ctrs[0].newInstance();
|
|
|
|
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
|
2012-10-25 17:34:24 -07:00
|
|
|
} else {
|
|
|
|
return new ConstantCallSite(
|
|
|
|
MethodHandles.Lookup.IMPL_LOOKUP
|
2013-02-16 12:36:54 -08:00
|
|
|
.findConstructor(innerClass, constructorType)
|
|
|
|
.asType(constructorType.changeReturnType(samBase)));
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a class file which implements the functional
|
|
|
|
* interface, define and return the class.
|
|
|
|
*
|
|
|
|
* @return a Class which implements the functional interface
|
2013-02-16 12:36:54 -08:00
|
|
|
* @throws LambdaConversionException If properly formed functional interface is not found
|
2012-10-25 17:34:24 -07:00
|
|
|
*/
|
2013-02-16 12:36:54 -08:00
|
|
|
private Class<?> spinInnerClass() throws LambdaConversionException {
|
2012-10-25 17:34:24 -07:00
|
|
|
String samName = samBase.getName().replace('.', '/');
|
2013-02-16 12:36:54 -08:00
|
|
|
String[] interfaces = new String[markerInterfaces.length + 1];
|
|
|
|
interfaces[0] = samName;
|
|
|
|
for (int i=0; i<markerInterfaces.length; i++) {
|
|
|
|
interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
|
|
|
|
}
|
2013-04-12 20:23:13 -07:00
|
|
|
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
|
2013-02-16 12:36:54 -08:00
|
|
|
lambdaClassName, null,
|
|
|
|
NAME_MAGIC_ACCESSOR_IMPL, interfaces);
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
// Generate final fields to be filled in by constructor
|
|
|
|
for (int i = 0; i < argTypes.length; i++) {
|
2013-02-16 12:36:54 -08:00
|
|
|
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(),
|
|
|
|
null, null);
|
2012-10-25 17:34:24 -07:00
|
|
|
fv.visitEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
generateConstructor();
|
|
|
|
|
|
|
|
MethodAnalyzer ma = new MethodAnalyzer();
|
|
|
|
|
|
|
|
// Forward the SAM method
|
|
|
|
if (ma.getSamMethod() == null) {
|
2013-02-16 12:36:54 -08:00
|
|
|
throw new LambdaConversionException(String.format("Functional interface method not found: %s", samMethodType));
|
2012-10-25 17:34:24 -07:00
|
|
|
} else {
|
|
|
|
generateForwardingMethod(ma.getSamMethod(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Forward the bridges
|
2013-02-16 12:36:54 -08:00
|
|
|
// @@@ The commented-out code is temporary, pending the VM's ability to bridge all methods on request
|
|
|
|
// @@@ Once the VM can do fail-over, uncomment the !ma.wasDefaultMethodFound() test, and emit the appropriate
|
|
|
|
// @@@ classfile attribute to request custom bridging. See 8002092.
|
|
|
|
if (!ma.getMethodsToBridge().isEmpty() /* && !ma.conflictFoundBetweenDefaultAndBridge() */ ) {
|
2012-10-25 17:34:24 -07:00
|
|
|
for (Method m : ma.getMethodsToBridge()) {
|
|
|
|
generateForwardingMethod(m, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSerializable) {
|
2013-02-16 12:36:54 -08:00
|
|
|
generateWriteReplace();
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
cw.visitEnd();
|
|
|
|
|
|
|
|
// Define the generated class in this VM.
|
|
|
|
|
|
|
|
final byte[] classBytes = cw.toByteArray();
|
|
|
|
|
2012-12-06 21:55:55 -08:00
|
|
|
/*** Uncomment to dump the generated file
|
2012-10-25 17:34:24 -07:00
|
|
|
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) {
|
2013-02-16 12:36:54 -08:00
|
|
|
PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName()).severe(ex.getMessage(), ex);
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
2012-12-06 21:55:55 -08:00
|
|
|
***/
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
ClassLoader loader = targetClass.getClassLoader();
|
2012-12-06 21:55:55 -08:00
|
|
|
ProtectionDomain pd = (loader == null)
|
|
|
|
? null
|
|
|
|
: AccessController.doPrivileged(
|
|
|
|
new PrivilegedAction<ProtectionDomain>() {
|
|
|
|
@Override
|
|
|
|
public ProtectionDomain run() {
|
|
|
|
return targetClass.getProtectionDomain();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2013-02-16 12:36:54 -08:00
|
|
|
return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
|
|
|
|
loader, pd);
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the constructor for the class
|
|
|
|
*/
|
|
|
|
private void generateConstructor() {
|
|
|
|
// Generate constructor
|
2012-12-06 21:55:55 -08:00
|
|
|
MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, constructorDesc, null, null);
|
2012-10-25 17:34:24 -07:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-02-16 12:36:54 -08:00
|
|
|
* Generate the writeReplace method (if needed for serialization)
|
2012-10-25 17:34:24 -07:00
|
|
|
*/
|
2013-02-16 12:36:54 -08:00
|
|
|
private void generateWriteReplace() {
|
|
|
|
TypeConvertingMethodAdapter mv
|
|
|
|
= new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
|
|
|
NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
|
|
|
|
null, null));
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
mv.visitCode();
|
|
|
|
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
|
2013-02-16 12:36:54 -08:00
|
|
|
mv.visitInsn(DUP);;
|
2013-02-26 10:38:58 -08:00
|
|
|
mv.visitLdcInsn(Type.getType(targetClass));
|
2013-02-16 12:36:54 -08:00
|
|
|
mv.visitLdcInsn(samInfo.getReferenceKind());
|
|
|
|
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
|
|
|
|
mv.visitLdcInsn(samInfo.getName());
|
|
|
|
mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString());
|
|
|
|
mv.visitLdcInsn(implInfo.getReferenceKind());
|
|
|
|
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
|
|
|
|
mv.visitLdcInsn(implInfo.getName());
|
|
|
|
mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
|
|
|
|
mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
mv.iconst(argTypes.length);
|
|
|
|
mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
|
|
|
|
for (int i = 0; i < argTypes.length; i++) {
|
2013-02-16 12:36:54 -08:00
|
|
|
mv.visitInsn(DUP);
|
2012-10-25 17:34:24 -07:00
|
|
|
mv.iconst(i);
|
|
|
|
mv.visitVarInsn(ALOAD, 0);
|
2013-02-16 12:36:54 -08:00
|
|
|
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
|
|
|
mv.boxIfTypePrimitive(argTypes[i]);
|
2012-10-25 17:34:24 -07:00
|
|
|
mv.visitInsn(AASTORE);
|
|
|
|
}
|
2013-02-16 12:36:54 -08:00
|
|
|
mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
|
|
|
|
DESCR_CTOR_SERIALIZED_LAMBDA);
|
2012-10-25 17:34:24 -07:00
|
|
|
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);
|
2013-02-16 12:36:54 -08:00
|
|
|
visitInsn(DUP);;
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
|
|
|
for (int i = 0; i < argTypes.length; i++) {
|
|
|
|
visitVarInsn(ALOAD, 0);
|
2013-02-16 12:36:54 -08:00
|
|
|
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
|
2012-10-25 17:34:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2013-02-16 12:36:54 -08:00
|
|
|
visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
|
2012-10-25 17:34:24 -07:00
|
|
|
|
|
|
|
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];
|
|
|
|
|
2013-02-16 12:36:54 -08:00
|
|
|
visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
|
2012-10-25 17:34:24 -07:00
|
|
|
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];
|
|
|
|
|
2013-02-16 12:36:54 -08:00
|
|
|
visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
|
2012-10-25 17:34:24 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|