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

Print this page

        

@@ -23,29 +23,26 @@
  * 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.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>";
 

@@ -78,55 +75,40 @@
     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.
+     * 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 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
+     * @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
+     * @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)
+                                       int flags,
+                                       Class<?>[] markerInterfaces)
             throws ReflectiveOperationException, LambdaConversionException {
-        super(caller, invokedType, samMethod, implMethod, instantiatedMethodType,
-              isSerializable, markerInterfaces, additionalBridges);
+        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,12 +132,11 @@
      * 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
+     * @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,20 +172,12 @@
 
     /**
      * 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
+     * @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,26 +195,32 @@
             fv.visitEnd();
         }
 
         generateConstructor();
 
+        MethodAnalyzer ma = new MethodAnalyzer();
+
         // Forward the SAM method
-        String methodDescriptor = samMethodType.toMethodDescriptorString();
-        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samInfo.getName(), methodDescriptor, null, null);
-        new ForwardingMethodGenerator(mv).generate(methodDescriptor);
+        if (ma.getSamMethod() == null) {
+            throw new LambdaConversionException(String.format("Functional interface method not found: %s", samMethodType));
+        } else {
+            generateForwardingMethod(ma.getSamMethod(), false);
+        }
 
         // 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);
+        // @@@ 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)
+        if (isSerializable) {
             generateWriteReplace();
+        }
 
         cw.visitEnd();
 
         // Define the generated class in this VM.
 

@@ -266,11 +245,11 @@
                     return targetClass.getProtectionDomain();
                 }
             }
         );
 
-        return UNSAFE.defineClass(lambdaClassName, classBytes, 0, classBytes.length,
+        return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
                                   loader, pd);
     }
 
     /**
      * Generate the constructor for the class

@@ -284,12 +263,11 @@
         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.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,11 +281,11 @@
                                                                  NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
                                                                  null, null));
 
         mv.visitCode();
         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
-        mv.visitInsn(DUP);
+        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,39 +311,57 @@
         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(String methodDescriptor) {
+        void generate(Method m) throws InternalError {
             visitCode();
 
             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
                 visitTypeInsn(NEW, implMethodClassName);
-                visitInsn(DUP);
+                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));
+            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(methodDescriptor);
+            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();