< prev index next >

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

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com

@@ -25,63 +25,65 @@
 
 package java.lang.invoke;
 
 import jdk.internal.org.objectweb.asm.*;
 import sun.invoke.util.BytecodeDescriptor;
-import jdk.internal.misc.Unsafe;
 import sun.security.action.GetPropertyAction;
 import sun.security.action.GetBooleanAction;
 
 import java.io.FilePermission;
 import java.io.Serializable;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.LinkedHashSet;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.PropertyPermission;
 import java.util.Set;
 
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
 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 = 52;
     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
     private static final String NAME_CTOR = "<init>";
-    private static final String NAME_FACTORY = "get$Lambda";
 
     //Serialization support
     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
     private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
     private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
     private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
+    private static final String DESCR_SET_IMPL_METHOD = "(Ljava/lang/invoke/MethodHandle;)V";
+
     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
     private static final String NAME_METHOD_READ_OBJECT = "readObject";
     private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
+    private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";
 
     private static final String DESCR_CLASS = "Ljava/lang/Class;";
     private static final String DESCR_STRING = "Ljava/lang/String;";
     private static final String DESCR_OBJECT = "Ljava/lang/Object;";
+    private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
             = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
             + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
 
     private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
     private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
 
-    private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
-
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     // Used to ensure that each spun class name is unique
     private static final AtomicInteger counter = new AtomicInteger(0);
 

@@ -106,10 +108,11 @@
     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
     private final ClassWriter cw;                    // ASM class writer
     private final String[] argNames;                 // Generated names for the constructor arguments
     private final String[] argDescs;                 // Type descriptors for the constructor arguments
     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
+    private final boolean useImplMethodHandle;       // use MethodHandle invocation instead of symbolic bytecode invocation
 
     /**
      * General meta-factory constructor, supporting both standard cases and
      * allowing for uncommon options such as serialization or bridging.
      *

@@ -161,11 +164,13 @@
               isSerializable, markerInterfaces, additionalBridges);
         implMethodClassName = implClass.getName().replace('.', '/');
         implMethodName = implInfo.getName();
         implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
         constructorType = invokedType.changeReturnType(Void.TYPE);
-        lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
+        lambdaClassName = lambdaClassName(targetClass);
+        useImplMethodHandle = !implClass.getPackageName().equals(implInfo.getDeclaringClass().getPackageName())
+                                && !Modifier.isPublic(implInfo.getModifiers());
         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
         int parameterCount = invokedType.parameterCount();
         if (parameterCount > 0) {
             argNames = new String[parameterCount];
             argDescs = new String[parameterCount];

@@ -176,10 +181,19 @@
         } else {
             argNames = argDescs = EMPTY_STRING_ARRAY;
         }
     }
 
+    private static String lambdaClassName(Class<?> targetClass) {
+        String name = targetClass.getName();
+        if (targetClass.isHiddenClass()) {
+            // use the original class name
+            name = name.replace('/', '_');
+        }
+        return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
+    }
+
     /**
      * 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.

@@ -215,24 +229,18 @@
             }
 
             try {
                 Object inst = ctrs[0].newInstance();
                 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
-            }
-            catch (ReflectiveOperationException e) {
+            } catch (ReflectiveOperationException e) {
                 throw new LambdaConversionException("Exception instantiating lambda object", e);
             }
         } else {
             try {
-                if (!disableEagerInitialization) {
-                    UNSAFE.ensureClassInitialized(innerClass);
-                }
-                return new ConstantCallSite(
-                        MethodHandles.Lookup.IMPL_LOOKUP
-                             .findStatic(innerClass, NAME_FACTORY, invokedType));
-            }
-            catch (ReflectiveOperationException e) {
+                MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
+                return new ConstantCallSite(mh.asType(invokedType));
+            } catch (ReflectiveOperationException e) {
                 throw new LambdaConversionException("Exception finding constructor", e);
             }
         }
     }
 

@@ -281,41 +289,51 @@
             fv.visitEnd();
         }
 
         generateConstructor();
 
-        if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
-            generateFactory();
-        }
-
         // Forward the SAM method
         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
                                           samMethodType.toMethodDescriptorString(), null, null);
-        mv.visitAnnotation(DESCR_HIDDEN, true);
         new ForwardingMethodGenerator(mv).generate(samMethodType);
 
         // Forward the bridges
         if (additionalBridges != null) {
             for (MethodType mt : additionalBridges) {
                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
                                     mt.toMethodDescriptorString(), null, null);
-                mv.visitAnnotation(DESCR_HIDDEN, true);
                 new ForwardingMethodGenerator(mv).generate(mt);
             }
         }
 
+        if (useImplMethodHandle) {
+            FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
+                                            NAME_FIELD_IMPL_METHOD,
+                                            DESCR_METHOD_HANDLE,
+                                            null, null);
+            fv.visitEnd();
+
+            mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC,
+                                "setImplMethod", DESCR_SET_IMPL_METHOD,
+                                null, null);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(PUTSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+        }
+
         if (isSerializable)
             generateSerializationFriendlyMethods();
         else if (accidentallySerializable)
             generateSerializationHostileMethods();
 
         cw.visitEnd();
 
         // Define the generated class in this VM.
 
         final byte[] classBytes = cw.toByteArray();
-
         // If requested, dump out to a file for debugging purposes
         if (dumper != null) {
             AccessController.doPrivileged(new PrivilegedAction<>() {
                 @Override
                 public Void run() {

@@ -325,32 +343,30 @@
             }, null,
             new FilePermission("<<ALL FILES>>", "read, write"),
             // createDirectories may need it
             new PropertyPermission("user.dir", "read"));
         }
-
-        return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
+        try {
+            // this class is linked at the indy callsite; so define a hidden nestmate
+            Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
+            if (useImplMethodHandle) {
+                // If the target class invokes a method reference this::m which is
+                // resolved to a protected method inherited from a superclass in a different
+                // package, the target class does not have a bridge and this method reference
+                // has been changed from public to protected after the target class was compiled.
+                // This lambda proxy class has no access to the resolved method.
+                // So this workaround by passing the live implMethod method handle
+                // to the proxy class to invoke directly.
+                MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
+                mh.invokeExact(implMethod);
+            }
+            return lookup.lookupClass();
+        } catch (IllegalAccessException e) {
+            throw new LambdaConversionException("Exception defining lambda proxy class", e);
+        } catch (Throwable t) {
+            throw new InternalError(t);
     }
-
-    /**
-     * Generate the factory method for the class
-     */
-    private void generateFactory() {
-        MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
-        m.visitCode();
-        m.visitTypeInsn(NEW, lambdaClassName);
-        m.visitInsn(Opcodes.DUP);
-        int parameterCount = invokedType.parameterCount();
-        for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
-            Class<?> argType = invokedType.parameterType(typeIndex);
-            m.visitVarInsn(getLoadOpcode(argType), varIndex);
-            varIndex += getParameterSize(argType);
-        }
-        m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
-        m.visitInsn(ARETURN);
-        m.visitMaxs(-1, -1);
-        m.visitEnd();
     }
 
     /**
      * Generate the constructor for the class
      */

@@ -462,22 +478,31 @@
 
             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
                 visitTypeInsn(NEW, implMethodClassName);
                 visitInsn(DUP);
             }
+            if (useImplMethodHandle) {
+                visitVarInsn(ALOAD, 0);
+                visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
+            }
             for (int i = 0; i < argNames.length; i++) {
                 visitVarInsn(ALOAD, 0);
                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
             }
 
             convertArgumentTypes(methodType);
 
+            if (useImplMethodHandle) {
+                MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
+                visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
+                                "invokeExact", mtype.descriptorString(), false);
+            } else {
             // Invoke the method we want to forward to
             visitMethodInsn(invocationOpcode(), implMethodClassName,
                             implMethodName, implMethodDesc,
                             implClass.isInterface());
-
+            }
             // 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
             Class<?> implReturnClass = implMethodType.returnType();
             Class<?> samReturnClass = methodType.returnType();
< prev index next >