jaroslav@1646: /* jaroslav@1646: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. jaroslav@1646: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1646: * jaroslav@1646: * This code is free software; you can redistribute it and/or modify it jaroslav@1646: * under the terms of the GNU General Public License version 2 only, as jaroslav@1646: * published by the Free Software Foundation. Oracle designates this jaroslav@1646: * particular file as subject to the "Classpath" exception as provided jaroslav@1646: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1646: * jaroslav@1646: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1646: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1646: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1646: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1646: * accompanied this code). jaroslav@1646: * jaroslav@1646: * You should have received a copy of the GNU General Public License version jaroslav@1646: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1646: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1646: * jaroslav@1646: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1646: * or visit www.oracle.com if you need additional information or have any jaroslav@1646: * questions. jaroslav@1646: */ jaroslav@1646: jaroslav@1646: package java.lang.invoke; jaroslav@1646: jaroslav@1646: import jdk.internal.org.objectweb.asm.*; jaroslav@1646: import sun.invoke.util.BytecodeDescriptor; jaroslav@1646: import sun.misc.Unsafe; jaroslav@1646: import sun.security.action.GetPropertyAction; jaroslav@1646: jaroslav@1646: import java.io.FilePermission; jaroslav@1646: import java.io.Serializable; jaroslav@1646: import java.lang.reflect.Constructor; jaroslav@1646: import java.security.AccessController; jaroslav@1646: import java.security.PrivilegedAction; jaroslav@1646: import java.util.LinkedHashSet; jaroslav@1646: import java.util.concurrent.atomic.AtomicInteger; jaroslav@1646: import java.util.PropertyPermission; jaroslav@1646: import java.util.Set; jaroslav@1646: jaroslav@1646: import static jdk.internal.org.objectweb.asm.Opcodes.*; jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Lambda metafactory implementation which dynamically creates an jaroslav@1646: * inner-class-like class per lambda callsite. jaroslav@1646: * jaroslav@1646: * @see LambdaMetafactory jaroslav@1646: */ jaroslav@1646: /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { jaroslav@1646: private static final Unsafe UNSAFE = Unsafe.getUnsafe(); jaroslav@1646: jaroslav@1646: private static final int CLASSFILE_VERSION = 52; jaroslav@1646: private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); jaroslav@1646: private static final String JAVA_LANG_OBJECT = "java/lang/Object"; jaroslav@1646: private static final String NAME_CTOR = ""; jaroslav@1646: private static final String NAME_FACTORY = "get$Lambda"; jaroslav@1646: jaroslav@1646: //Serialization support jaroslav@1646: private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda"; jaroslav@1646: private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException"; jaroslav@1646: private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; jaroslav@1646: private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V"; jaroslav@1646: private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V"; jaroslav@1646: private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; jaroslav@1646: private static final String NAME_METHOD_READ_OBJECT = "readObject"; jaroslav@1646: private static final String NAME_METHOD_WRITE_OBJECT = "writeObject"; jaroslav@1646: private static final String DESCR_CTOR_SERIALIZED_LAMBDA jaroslav@1646: = MethodType.methodType(void.class, jaroslav@1646: Class.class, jaroslav@1646: String.class, String.class, String.class, jaroslav@1646: int.class, String.class, String.class, String.class, jaroslav@1646: String.class, jaroslav@1646: Object[].class).toMethodDescriptorString(); jaroslav@1646: private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION jaroslav@1646: = MethodType.methodType(void.class, String.class).toMethodDescriptorString(); jaroslav@1646: private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION}; jaroslav@1646: jaroslav@1646: jaroslav@1646: private static final String[] EMPTY_STRING_ARRAY = new String[0]; jaroslav@1646: jaroslav@1646: // Used to ensure that each spun class name is unique jaroslav@1646: private static final AtomicInteger counter = new AtomicInteger(0); jaroslav@1646: jaroslav@1646: // For dumping generated classes to disk, for debugging purposes jaroslav@1646: private static final ProxyClassesDumper dumper; jaroslav@1646: jaroslav@1646: static { jaroslav@1646: final String key = "jdk.internal.lambda.dumpProxyClasses"; jaroslav@1646: String path = AccessController.doPrivileged( jaroslav@1646: new GetPropertyAction(key), null, jaroslav@1646: new PropertyPermission(key , "read")); jaroslav@1646: dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // See context values in AbstractValidatingLambdaMetafactory jaroslav@1646: private final String implMethodClassName; // Name of type containing implementation "CC" jaroslav@1646: private final String implMethodName; // Name of implementation method "impl" jaroslav@1646: private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" jaroslav@1646: private final Class implMethodReturnClass; // class for implementaion method return type "Ljava/lang/String;" jaroslav@1646: private final MethodType constructorType; // Generated class constructor type "(CC)void" jaroslav@1646: private final ClassWriter cw; // ASM class writer jaroslav@1646: private final String[] argNames; // Generated names for the constructor arguments jaroslav@1646: private final String[] argDescs; // Type descriptors for the constructor arguments jaroslav@1646: private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * General meta-factory constructor, supporting both standard cases and jaroslav@1646: * allowing for uncommon options such as serialization or bridging. jaroslav@1646: * jaroslav@1646: * @param caller Stacked automatically by VM; represents a lookup context jaroslav@1646: * with the accessibility privileges of the caller. jaroslav@1646: * @param invokedType Stacked automatically by VM; the signature of the jaroslav@1646: * invoked method, which includes the expected static jaroslav@1646: * type of the returned lambda object, and the static jaroslav@1646: * types of the captured arguments for the lambda. In jaroslav@1646: * the event that the implementation method is an jaroslav@1646: * instance method, the first argument in the invocation jaroslav@1646: * signature will correspond to the receiver. jaroslav@1646: * @param samMethodName Name of the method in the functional interface to jaroslav@1646: * which the lambda or method reference is being jaroslav@1646: * converted, represented as a String. jaroslav@1646: * @param samMethodType Type of the method in the functional interface to jaroslav@1646: * which the lambda or method reference is being jaroslav@1646: * converted, represented as a MethodType. jaroslav@1646: * @param implMethod The implementation method which should be called (with jaroslav@1646: * suitable adaptation of argument types, return types, jaroslav@1646: * and adjustment for captured arguments) when methods of jaroslav@1646: * the resulting functional interface instance are invoked. jaroslav@1646: * @param instantiatedMethodType The signature of the primary functional jaroslav@1646: * interface method after type variables are jaroslav@1646: * substituted with their instantiation from jaroslav@1646: * the capture site jaroslav@1646: * @param isSerializable Should the lambda be made serializable? If set, jaroslav@1646: * either the target type or one of the additional SAM jaroslav@1646: * types must extend {@code Serializable}. jaroslav@1646: * @param markerInterfaces Additional interfaces which the lambda object jaroslav@1646: * should implement. jaroslav@1646: * @param additionalBridges Method types for additional signatures to be jaroslav@1646: * bridged to the implementation method jaroslav@1646: * @throws LambdaConversionException If any of the meta-factory protocol jaroslav@1646: * invariants are violated jaroslav@1646: */ jaroslav@1646: public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, jaroslav@1646: MethodType invokedType, jaroslav@1646: String samMethodName, jaroslav@1646: MethodType samMethodType, jaroslav@1646: MethodHandle implMethod, jaroslav@1646: MethodType instantiatedMethodType, jaroslav@1646: boolean isSerializable, jaroslav@1646: Class[] markerInterfaces, jaroslav@1646: MethodType[] additionalBridges) jaroslav@1646: throws LambdaConversionException { jaroslav@1646: super(caller, invokedType, samMethodName, samMethodType, jaroslav@1646: implMethod, instantiatedMethodType, jaroslav@1646: isSerializable, markerInterfaces, additionalBridges); jaroslav@1646: implMethodClassName = implDefiningClass.getName().replace('.', '/'); jaroslav@1646: implMethodName = implInfo.getName(); jaroslav@1646: implMethodDesc = implMethodType.toMethodDescriptorString(); jaroslav@1646: implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial) jaroslav@1646: ? implDefiningClass jaroslav@1646: : implMethodType.returnType(); jaroslav@1646: constructorType = invokedType.changeReturnType(Void.TYPE); jaroslav@1646: lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); jaroslav@1646: cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); jaroslav@1646: int parameterCount = invokedType.parameterCount(); jaroslav@1646: if (parameterCount > 0) { jaroslav@1646: argNames = new String[parameterCount]; jaroslav@1646: argDescs = new String[parameterCount]; jaroslav@1646: for (int i = 0; i < parameterCount; i++) { jaroslav@1646: argNames[i] = "arg$" + (i + 1); jaroslav@1646: argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i)); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: argNames = argDescs = EMPTY_STRING_ARRAY; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Build the CallSite. Generate a class file which implements the functional jaroslav@1646: * interface, define the class, if there are no parameters create an instance jaroslav@1646: * of the class which the CallSite will return, otherwise, generate handles jaroslav@1646: * which will call the class' constructor. jaroslav@1646: * jaroslav@1646: * @return a CallSite, which, when invoked, will return an instance of the jaroslav@1646: * functional interface jaroslav@1646: * @throws ReflectiveOperationException jaroslav@1646: * @throws LambdaConversionException If properly formed functional interface jaroslav@1646: * is not found jaroslav@1646: */ jaroslav@1646: @Override jaroslav@1646: CallSite buildCallSite() throws LambdaConversionException { jaroslav@1646: final Class innerClass = spinInnerClass(); jaroslav@1646: if (invokedType.parameterCount() == 0) { jaroslav@1646: final Constructor[] ctrs = AccessController.doPrivileged( jaroslav@1646: new PrivilegedAction() { jaroslav@1646: @Override jaroslav@1646: public Constructor[] run() { jaroslav@1646: Constructor[] ctrs = innerClass.getDeclaredConstructors(); jaroslav@1646: if (ctrs.length == 1) { jaroslav@1646: // The lambda implementing inner class constructor is private, set jaroslav@1646: // it accessible (by us) before creating the constant sole instance jaroslav@1646: ctrs[0].setAccessible(true); jaroslav@1646: } jaroslav@1646: return ctrs; jaroslav@1646: } jaroslav@1646: }); jaroslav@1646: if (ctrs.length != 1) { jaroslav@1646: throw new LambdaConversionException("Expected one lambda constructor for " jaroslav@1646: + innerClass.getCanonicalName() + ", got " + ctrs.length); jaroslav@1646: } jaroslav@1646: jaroslav@1646: try { jaroslav@1646: Object inst = ctrs[0].newInstance(); jaroslav@1646: return new ConstantCallSite(MethodHandles.constant(samBase, inst)); jaroslav@1646: } jaroslav@1646: catch (ReflectiveOperationException e) { jaroslav@1646: throw new LambdaConversionException("Exception instantiating lambda object", e); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: try { jaroslav@1646: UNSAFE.ensureClassInitialized(innerClass); jaroslav@1646: return new ConstantCallSite( jaroslav@1646: MethodHandles.Lookup.IMPL_LOOKUP jaroslav@1646: .findStatic(innerClass, NAME_FACTORY, invokedType)); jaroslav@1646: } jaroslav@1646: catch (ReflectiveOperationException e) { jaroslav@1646: throw new LambdaConversionException("Exception finding constructor", e); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Generate a class file which implements the functional jaroslav@1646: * interface, define and return the class. jaroslav@1646: * jaroslav@1646: * @implNote The class that is generated does not include signature jaroslav@1646: * information for exceptions that may be present on the SAM method. jaroslav@1646: * This is to reduce classfile size, and is harmless as checked exceptions jaroslav@1646: * are erased anyway, no one will ever compile against this classfile, jaroslav@1646: * and we make no guarantees about the reflective properties of lambda jaroslav@1646: * objects. jaroslav@1646: * jaroslav@1646: * @return a Class which implements the functional interface jaroslav@1646: * @throws LambdaConversionException If properly formed functional interface jaroslav@1646: * is not found jaroslav@1646: */ jaroslav@1646: private Class spinInnerClass() throws LambdaConversionException { jaroslav@1646: String[] interfaces; jaroslav@1646: String samIntf = samBase.getName().replace('.', '/'); jaroslav@1646: boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase); jaroslav@1646: if (markerInterfaces.length == 0) { jaroslav@1646: interfaces = new String[]{samIntf}; jaroslav@1646: } else { jaroslav@1646: // Assure no duplicate interfaces (ClassFormatError) jaroslav@1646: Set itfs = new LinkedHashSet<>(markerInterfaces.length + 1); jaroslav@1646: itfs.add(samIntf); jaroslav@1646: for (Class markerInterface : markerInterfaces) { jaroslav@1646: itfs.add(markerInterface.getName().replace('.', '/')); jaroslav@1646: accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface); jaroslav@1646: } jaroslav@1646: interfaces = itfs.toArray(new String[itfs.size()]); jaroslav@1646: } jaroslav@1646: jaroslav@1646: cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, jaroslav@1646: lambdaClassName, null, jaroslav@1646: JAVA_LANG_OBJECT, interfaces); jaroslav@1646: jaroslav@1646: // Generate final fields to be filled in by constructor jaroslav@1646: for (int i = 0; i < argDescs.length; i++) { jaroslav@1646: FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, jaroslav@1646: argNames[i], jaroslav@1646: argDescs[i], jaroslav@1646: null, null); jaroslav@1646: fv.visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: generateConstructor(); jaroslav@1646: jaroslav@1646: if (invokedType.parameterCount() != 0) { jaroslav@1646: generateFactory(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Forward the SAM method jaroslav@1646: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, jaroslav@1646: samMethodType.toMethodDescriptorString(), null, null); jaroslav@1646: new ForwardingMethodGenerator(mv).generate(samMethodType); jaroslav@1646: jaroslav@1646: // Forward the bridges jaroslav@1646: if (additionalBridges != null) { jaroslav@1646: for (MethodType mt : additionalBridges) { jaroslav@1646: mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, jaroslav@1646: mt.toMethodDescriptorString(), null, null); jaroslav@1646: new ForwardingMethodGenerator(mv).generate(mt); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: if (isSerializable) jaroslav@1646: generateSerializationFriendlyMethods(); jaroslav@1646: else if (accidentallySerializable) jaroslav@1646: generateSerializationHostileMethods(); jaroslav@1646: jaroslav@1646: cw.visitEnd(); jaroslav@1646: jaroslav@1646: // Define the generated class in this VM. jaroslav@1646: jaroslav@1646: final byte[] classBytes = cw.toByteArray(); jaroslav@1646: jaroslav@1646: // If requested, dump out to a file for debugging purposes jaroslav@1646: if (dumper != null) { jaroslav@1646: AccessController.doPrivileged(new PrivilegedAction() { jaroslav@1646: @Override jaroslav@1646: public Void run() { jaroslav@1646: dumper.dumpClass(lambdaClassName, classBytes); jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: }, null, jaroslav@1646: new FilePermission("<>", "read, write"), jaroslav@1646: // createDirectories may need it jaroslav@1646: new PropertyPermission("user.dir", "read")); jaroslav@1646: } jaroslav@1646: jaroslav@1646: return UNSAFE.defineAnonymousClass(targetClass, classBytes, null); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Generate the factory method for the class jaroslav@1646: */ jaroslav@1646: private void generateFactory() { jaroslav@1646: MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null); jaroslav@1646: m.visitCode(); jaroslav@1646: m.visitTypeInsn(NEW, lambdaClassName); jaroslav@1646: m.visitInsn(Opcodes.DUP); jaroslav@1646: int parameterCount = invokedType.parameterCount(); jaroslav@1646: for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) { jaroslav@1646: Class argType = invokedType.parameterType(typeIndex); jaroslav@1646: m.visitVarInsn(getLoadOpcode(argType), varIndex); jaroslav@1646: varIndex += getParameterSize(argType); jaroslav@1646: } jaroslav@1646: m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString()); jaroslav@1646: m.visitInsn(ARETURN); jaroslav@1646: m.visitMaxs(-1, -1); jaroslav@1646: m.visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Generate the constructor for the class jaroslav@1646: */ jaroslav@1646: private void generateConstructor() { jaroslav@1646: // Generate constructor jaroslav@1646: MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, jaroslav@1646: constructorType.toMethodDescriptorString(), null, null); jaroslav@1646: ctor.visitCode(); jaroslav@1646: ctor.visitVarInsn(ALOAD, 0); jaroslav@1646: ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR, jaroslav@1646: METHOD_DESCRIPTOR_VOID); jaroslav@1646: int parameterCount = invokedType.parameterCount(); jaroslav@1646: for (int i = 0, lvIndex = 0; i < parameterCount; i++) { jaroslav@1646: ctor.visitVarInsn(ALOAD, 0); jaroslav@1646: Class argType = invokedType.parameterType(i); jaroslav@1646: ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1); jaroslav@1646: lvIndex += getParameterSize(argType); jaroslav@1646: ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]); jaroslav@1646: } jaroslav@1646: ctor.visitInsn(RETURN); jaroslav@1646: // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored jaroslav@1646: ctor.visitMaxs(-1, -1); jaroslav@1646: ctor.visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Generate a writeReplace method that supports serialization jaroslav@1646: */ jaroslav@1646: private void generateSerializationFriendlyMethods() { jaroslav@1646: TypeConvertingMethodAdapter mv jaroslav@1646: = new TypeConvertingMethodAdapter( jaroslav@1646: cw.visitMethod(ACC_PRIVATE + ACC_FINAL, jaroslav@1646: NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, jaroslav@1646: null, null)); jaroslav@1646: jaroslav@1646: mv.visitCode(); jaroslav@1646: mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); jaroslav@1646: mv.visitInsn(DUP); jaroslav@1646: mv.visitLdcInsn(Type.getType(targetClass)); jaroslav@1646: mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); jaroslav@1646: mv.visitLdcInsn(samMethodName); jaroslav@1646: mv.visitLdcInsn(samMethodType.toMethodDescriptorString()); jaroslav@1646: mv.visitLdcInsn(implInfo.getReferenceKind()); jaroslav@1646: mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/')); jaroslav@1646: mv.visitLdcInsn(implInfo.getName()); jaroslav@1646: mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString()); jaroslav@1646: mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString()); jaroslav@1646: mv.iconst(argDescs.length); jaroslav@1646: mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT); jaroslav@1646: for (int i = 0; i < argDescs.length; i++) { jaroslav@1646: mv.visitInsn(DUP); jaroslav@1646: mv.iconst(i); jaroslav@1646: mv.visitVarInsn(ALOAD, 0); jaroslav@1646: mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); jaroslav@1646: mv.boxIfTypePrimitive(Type.getType(argDescs[i])); jaroslav@1646: mv.visitInsn(AASTORE); jaroslav@1646: } jaroslav@1646: mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR, jaroslav@1646: DESCR_CTOR_SERIALIZED_LAMBDA); jaroslav@1646: mv.visitInsn(ARETURN); jaroslav@1646: // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored jaroslav@1646: mv.visitMaxs(-1, -1); jaroslav@1646: mv.visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Generate a readObject/writeObject method that is hostile to serialization jaroslav@1646: */ jaroslav@1646: private void generateSerializationHostileMethods() { jaroslav@1646: MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, jaroslav@1646: NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT, jaroslav@1646: null, SER_HOSTILE_EXCEPTIONS); jaroslav@1646: mv.visitCode(); jaroslav@1646: mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); jaroslav@1646: mv.visitInsn(DUP); jaroslav@1646: mv.visitLdcInsn("Non-serializable lambda"); jaroslav@1646: mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, jaroslav@1646: DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION); jaroslav@1646: mv.visitInsn(ATHROW); jaroslav@1646: mv.visitMaxs(-1, -1); jaroslav@1646: mv.visitEnd(); jaroslav@1646: jaroslav@1646: mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, jaroslav@1646: NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT, jaroslav@1646: null, SER_HOSTILE_EXCEPTIONS); jaroslav@1646: mv.visitCode(); jaroslav@1646: mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION); jaroslav@1646: mv.visitInsn(DUP); jaroslav@1646: mv.visitLdcInsn("Non-serializable lambda"); jaroslav@1646: mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR, jaroslav@1646: DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION); jaroslav@1646: mv.visitInsn(ATHROW); jaroslav@1646: mv.visitMaxs(-1, -1); jaroslav@1646: mv.visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * This class generates a method body which calls the lambda implementation jaroslav@1646: * method, converting arguments, as needed. jaroslav@1646: */ jaroslav@1646: private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { jaroslav@1646: jaroslav@1646: ForwardingMethodGenerator(MethodVisitor mv) { jaroslav@1646: super(mv); jaroslav@1646: } jaroslav@1646: jaroslav@1646: void generate(MethodType methodType) { jaroslav@1646: visitCode(); jaroslav@1646: jaroslav@1646: if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { jaroslav@1646: visitTypeInsn(NEW, implMethodClassName); jaroslav@1646: visitInsn(DUP); jaroslav@1646: } jaroslav@1646: for (int i = 0; i < argNames.length; i++) { jaroslav@1646: visitVarInsn(ALOAD, 0); jaroslav@1646: visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]); jaroslav@1646: } jaroslav@1646: jaroslav@1646: convertArgumentTypes(methodType); jaroslav@1646: jaroslav@1646: // Invoke the method we want to forward to jaroslav@1646: visitMethodInsn(invocationOpcode(), implMethodClassName, jaroslav@1646: implMethodName, implMethodDesc, jaroslav@1646: implDefiningClass.isInterface()); jaroslav@1646: jaroslav@1646: // Convert the return value (if any) and return it jaroslav@1646: // Note: if adapting from non-void to void, the 'return' jaroslav@1646: // instruction will pop the unneeded result jaroslav@1646: Class samReturnClass = methodType.returnType(); jaroslav@1646: convertType(implMethodReturnClass, samReturnClass, samReturnClass); jaroslav@1646: visitInsn(getReturnOpcode(samReturnClass)); jaroslav@1646: // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored jaroslav@1646: visitMaxs(-1, -1); jaroslav@1646: visitEnd(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private void convertArgumentTypes(MethodType samType) { jaroslav@1646: int lvIndex = 0; jaroslav@1646: boolean samIncludesReceiver = implIsInstanceMethod && jaroslav@1646: invokedType.parameterCount() == 0; jaroslav@1646: int samReceiverLength = samIncludesReceiver ? 1 : 0; jaroslav@1646: if (samIncludesReceiver) { jaroslav@1646: // push receiver jaroslav@1646: Class rcvrType = samType.parameterType(0); jaroslav@1646: visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1); jaroslav@1646: lvIndex += getParameterSize(rcvrType); jaroslav@1646: convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0)); jaroslav@1646: } jaroslav@1646: int samParametersLength = samType.parameterCount(); jaroslav@1646: int argOffset = implMethodType.parameterCount() - samParametersLength; jaroslav@1646: for (int i = samReceiverLength; i < samParametersLength; i++) { jaroslav@1646: Class argType = samType.parameterType(i); jaroslav@1646: visitVarInsn(getLoadOpcode(argType), lvIndex + 1); jaroslav@1646: lvIndex += getParameterSize(argType); jaroslav@1646: convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i)); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: private int invocationOpcode() throws InternalError { jaroslav@1646: switch (implKind) { jaroslav@1646: case MethodHandleInfo.REF_invokeStatic: jaroslav@1646: return INVOKESTATIC; jaroslav@1646: case MethodHandleInfo.REF_newInvokeSpecial: jaroslav@1646: return INVOKESPECIAL; jaroslav@1646: case MethodHandleInfo.REF_invokeVirtual: jaroslav@1646: return INVOKEVIRTUAL; jaroslav@1646: case MethodHandleInfo.REF_invokeInterface: jaroslav@1646: return INVOKEINTERFACE; jaroslav@1646: case MethodHandleInfo.REF_invokeSpecial: jaroslav@1646: return INVOKESPECIAL; jaroslav@1646: default: jaroslav@1646: throw new InternalError("Unexpected invocation kind: " + implKind); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: static int getParameterSize(Class c) { jaroslav@1646: if (c == Void.TYPE) { jaroslav@1646: return 0; jaroslav@1646: } else if (c == Long.TYPE || c == Double.TYPE) { jaroslav@1646: return 2; jaroslav@1646: } jaroslav@1646: return 1; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static int getLoadOpcode(Class c) { jaroslav@1646: if(c == Void.TYPE) { jaroslav@1646: throw new InternalError("Unexpected void type of load opcode"); jaroslav@1646: } jaroslav@1646: return ILOAD + getOpcodeOffset(c); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static int getReturnOpcode(Class c) { jaroslav@1646: if(c == Void.TYPE) { jaroslav@1646: return RETURN; jaroslav@1646: } jaroslav@1646: return IRETURN + getOpcodeOffset(c); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static int getOpcodeOffset(Class c) { jaroslav@1646: if (c.isPrimitive()) { jaroslav@1646: if (c == Long.TYPE) { jaroslav@1646: return 1; jaroslav@1646: } else if (c == Float.TYPE) { jaroslav@1646: return 2; jaroslav@1646: } else if (c == Double.TYPE) { jaroslav@1646: return 3; jaroslav@1646: } jaroslav@1646: return 0; jaroslav@1646: } else { jaroslav@1646: return 4; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: }