src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java

Print this page

        

*** 23,51 **** * questions. */ package java.lang.invoke; import jdk.internal.org.objectweb.asm.*; import sun.misc.Unsafe; - - import java.lang.reflect.Constructor; import java.security.AccessController; import java.security.PrivilegedAction; - import java.security.ProtectionDomain; - import java.util.concurrent.atomic.AtomicInteger; - import static jdk.internal.org.objectweb.asm.Opcodes.*; - /** * Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite. * * @see LambdaMetafactory */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - 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>"; --- 23,48 ---- * questions. */ package java.lang.invoke; + import java.lang.reflect.Constructor; + 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; import java.security.AccessController; import java.security.PrivilegedAction; /** * Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite. * * @see LambdaMetafactory */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 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>";
*** 78,132 **** 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 /** ! * General meta-factory constructor, supporting both standard cases and ! * allowing for uncommon options such as serialization or bridging. * ! * @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 primary functional ! * interface method after type variables are ! * substituted with their instantiation from ! * the capture site ! * @param isSerializable Should the lambda be made serializable? If set, ! * either the target type or one of the additional SAM ! * types must extend {@code Serializable}. ! * @param markerInterfaces Additional interfaces which the lambda object ! * should implement. ! * @param additionalBridges Method types for additional signatures to be ! * bridged to the implementation method * @throws ReflectiveOperationException ! * @throws LambdaConversionException If any of the meta-factory protocol ! * invariants are violated */ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, MethodHandle samMethod, MethodHandle implMethod, MethodType instantiatedMethodType, ! boolean isSerializable, ! Class<?>[] markerInterfaces, ! MethodType[] additionalBridges) throws ReflectiveOperationException, LambdaConversionException { ! super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, ! isSerializable, markerInterfaces, additionalBridges); implMethodClassName = implDefiningClass.getName().replace('.', '/'); implMethodName = implInfo.getName(); implMethodDesc = implMethodType.toMethodDescriptorString(); Type implMethodAsmType = Type.getMethodType(implMethodDesc); implMethodArgumentTypes = implMethodAsmType.getArgumentTypes(); --- 75,114 ---- 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 /** ! * General meta-factory constructor, standard cases and allowing for uncommon options such as serialization. * ! * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges ! * 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 primary functional interface method after type variables ! * are substituted with their instantiation from the capture site ! * @param flags A bitmask containing flags that may influence the translation of this lambda expression. Defined ! * fields include FLAG_SERIALIZABLE. ! * @param markerInterfaces Additional interfaces which the lambda object should implement. * @throws ReflectiveOperationException ! * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated */ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, MethodHandle samMethod, MethodHandle implMethod, MethodType instantiatedMethodType, ! int flags, ! Class<?>[] markerInterfaces) throws ReflectiveOperationException, LambdaConversionException { ! super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces); implMethodClassName = implDefiningClass.getName().replace('.', '/'); implMethodName = implInfo.getName(); implMethodDesc = implMethodType.toMethodDescriptorString(); Type implMethodAsmType = Type.getMethodType(implMethodDesc); implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
*** 150,161 **** * which will call the class' constructor. * * @return a CallSite, which, when invoked, will return an instance of the * functional interface * @throws ReflectiveOperationException ! * @throws LambdaConversionException If properly formed functional interface ! * is not found */ @Override CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException { final Class<?> innerClass = spinInnerClass(); if (invokedType.parameterCount() == 0) { --- 132,142 ---- * which will call the class' constructor. * * @return a CallSite, which, when invoked, will return an instance of the * functional interface * @throws ReflectiveOperationException ! * @throws LambdaConversionException If properly formed functional interface is not found */ @Override CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException { final Class<?> innerClass = spinInnerClass(); if (invokedType.parameterCount() == 0) {
*** 191,210 **** /** * Generate a class file which implements the functional * interface, define and return the class. * - * @implNote The class that is generated does not include signature - * information for exceptions that may be present on the SAM method. - * This is to reduce classfile size, and is harmless as checked exceptions - * are erased anyway, no one will ever compile against this classfile, - * and we make no guarantees about the reflective properties of lambda - * objects. - * * @return a Class which implements the functional interface ! * @throws LambdaConversionException If properly formed functional interface ! * is not found */ private Class<?> spinInnerClass() throws LambdaConversionException { String samName = samBase.getName().replace('.', '/'); String[] interfaces = new String[markerInterfaces.length + 1]; interfaces[0] = samName; --- 172,183 ---- /** * Generate a class file which implements the functional * interface, define and return the class. * * @return a Class which implements the functional interface ! * @throws LambdaConversionException If properly formed functional interface is not found */ private Class<?> spinInnerClass() throws LambdaConversionException { String samName = samBase.getName().replace('.', '/'); String[] interfaces = new String[markerInterfaces.length + 1]; interfaces[0] = samName;
*** 222,247 **** fv.visitEnd(); } generateConstructor(); // Forward the SAM method ! String methodDescriptor = samMethodType.toMethodDescriptorString(); ! MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samInfo.getName(), methodDescriptor, null, null); ! new ForwardingMethodGenerator(mv).generate(methodDescriptor); // Forward the bridges ! if (additionalBridges != null) { ! for (MethodType mt : additionalBridges) { ! methodDescriptor = mt.toMethodDescriptorString(); ! mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samInfo.getName(), methodDescriptor, null, null); ! new ForwardingMethodGenerator(mv).generate(methodDescriptor); } } ! if (isSerializable) generateWriteReplace(); cw.visitEnd(); // Define the generated class in this VM. --- 195,226 ---- fv.visitEnd(); } generateConstructor(); + MethodAnalyzer ma = new MethodAnalyzer(); + // Forward the SAM method ! if (ma.getSamMethod() == null) { ! throw new LambdaConversionException(String.format("Functional interface method not found: %s", samMethodType)); ! } else { ! generateForwardingMethod(ma.getSamMethod(), false); ! } // Forward the bridges ! // @@@ 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() */ ) { ! for (Method m : ma.getMethodsToBridge()) { ! generateForwardingMethod(m, true); } } ! if (isSerializable) { generateWriteReplace(); + } cw.visitEnd(); // Define the generated class in this VM.
*** 266,276 **** return targetClass.getProtectionDomain(); } } ); ! return UNSAFE.defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd); } /** * Generate the constructor for the class --- 245,255 ---- return targetClass.getProtectionDomain(); } } ); ! return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd); } /** * Generate the constructor for the class
*** 284,295 **** 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(); } --- 263,273 ---- 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(); }
*** 303,313 **** NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null)); mv.visitCode(); mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); ! mv.visitInsn(DUP); mv.visitLdcInsn(Type.getType(targetClass)); mv.visitLdcInsn(samInfo.getReferenceKind()); mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); mv.visitLdcInsn(samInfo.getName()); mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString()); --- 281,291 ---- NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null)); mv.visitCode(); mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); ! mv.visitInsn(DUP);; mv.visitLdcInsn(Type.getType(targetClass)); mv.visitLdcInsn(samInfo.getReferenceKind()); mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); mv.visitLdcInsn(samInfo.getName()); mv.visitLdcInsn(samInfo.getMethodType().toMethodDescriptorString());
*** 333,371 **** mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored mv.visitEnd(); } /** * 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(String methodDescriptor) { visitCode(); if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { visitTypeInsn(NEW, implMethodClassName); ! visitInsn(DUP); } for (int i = 0; i < argTypes.length; i++) { visitVarInsn(ALOAD, 0); visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor()); } ! convertArgumentTypes(Type.getArgumentTypes(methodDescriptor)); // 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(methodDescriptor); convertType(implMethodReturnType, samReturnType, samReturnType); visitInsn(samReturnType.getOpcode(Opcodes.IRETURN)); visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored visitEnd(); --- 311,367 ---- 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); ! visitInsn(DUP);; } for (int i = 0; i < argTypes.length; i++) { visitVarInsn(ALOAD, 0); visitFieldInsn(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); visitInsn(samReturnType.getOpcode(Opcodes.IRETURN)); visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored visitEnd();