< prev index next >

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

Print this page
*** 23,10 ***
--- 23,11 ---
   * questions.
   */
  
  package java.lang.invoke;
  
+ import jdk.internal.access.JavaLangInvokeAccess.ReflectableLambdaInfo;
  import jdk.internal.constant.ClassOrInterfaceDescImpl;
  import jdk.internal.misc.CDS;
  import jdk.internal.util.ClassFileDumper;
  import sun.invoke.util.VerifyAccess;
  

*** 35,10 ***
--- 36,12 ---
  import java.lang.classfile.ClassFile;
  import java.lang.classfile.CodeBuilder;
  import java.lang.classfile.MethodBuilder;
  import java.lang.classfile.Opcode;
  import java.lang.classfile.TypeKind;
+ import java.lang.classfile.constantpool.MethodHandleEntry;
+ import java.lang.classfile.constantpool.NameAndTypeEntry;
  import java.lang.constant.ClassDesc;
  import java.lang.constant.MethodTypeDesc;
  import java.lang.reflect.Modifier;
  import java.util.LinkedHashSet;
  import java.util.List;

*** 67,15 ***
--- 70,27 ---
  /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
      private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
      private static final @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"};
      private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC;
  
+     // Static builders to avoid lambdas
+     record MethodBody(Consumer<CodeBuilder> code) implements Consumer<MethodBuilder> {
+         @Override
+         public void accept(MethodBuilder mb) {
+             mb.withCode(code);
+         }
+     };
+ 
      // For dumping generated classes to disk, for debugging purposes
      private static final ClassFileDumper lambdaProxyClassFileDumper;
  
      private static final boolean disableEagerInitialization;
  
+     private static final String NAME_METHOD_QUOTED = "__internal_quoted";
+     private static final String quotedInstanceFieldName = "quoted";
+     private static final String COMPILER_GENERATED_MODEL_FIELD_NAME = "COMPILER_GENERATED_MODEL";
+ 
      static {
          // To dump the lambda proxy classes, set this system property:
          //    -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
          // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
          final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles";

*** 140,15 ***
                                         MethodType interfaceMethodType,
                                         MethodHandle implementation,
                                         MethodType dynamicMethodType,
                                         boolean isSerializable,
                                         Class<?>[] altInterfaces,
!                                        MethodType[] altMethods)
              throws LambdaConversionException {
          super(caller, factoryType, interfaceMethodName, interfaceMethodType,
                implementation, dynamicMethodType,
!               isSerializable, altInterfaces, altMethods);
          implMethodClassDesc = implClassDesc(implClass);
          implMethodName = implInfo.getName();
          implMethodDesc = methodDesc(implInfo.getMethodType());
          constructorType = factoryType.changeReturnType(Void.TYPE);
          lambdaClassName = lambdaClassName(targetClass);
--- 155,16 ---
                                         MethodType interfaceMethodType,
                                         MethodHandle implementation,
                                         MethodType dynamicMethodType,
                                         boolean isSerializable,
                                         Class<?>[] altInterfaces,
!                                        MethodType[] altMethods,
+                                        ReflectableLambdaInfo reflectableLambdaInfo)
              throws LambdaConversionException {
          super(caller, factoryType, interfaceMethodName, interfaceMethodType,
                implementation, dynamicMethodType,
!               isSerializable, altInterfaces, altMethods, reflectableLambdaInfo);
          implMethodClassDesc = implClassDesc(implClass);
          implMethodName = implInfo.getName();
          implMethodDesc = methodDesc(implInfo.getMethodType());
          constructorType = factoryType.changeReturnType(Void.TYPE);
          lambdaClassName = lambdaClassName(targetClass);

*** 315,15 ***
                  // Generate final fields to be filled in by constructor
                  for (int i = 0; i < argDescs.length; i++) {
                      clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL);
                  }
  
                  generateConstructor(clb);
  
!                 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
!                     generateClassInitializer(clb);
-                 }
  
                  // Forward the SAM method
                  clb.withMethodBody(interfaceMethodName,
                          methodDesc(interfaceMethodType),
                          ACC_PUBLIC,
--- 331,19 ---
                  // Generate final fields to be filled in by constructor
                  for (int i = 0; i < argDescs.length; i++) {
                      clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL);
                  }
  
+                 // if quotable, generate the field that will hold the value of quoted
+                 if (reflectableLambdaInfo != null) {
+                     clb.withField(quotedInstanceFieldName, reflectableLambdaInfo.quotedClass(), ACC_PRIVATE + ACC_FINAL);
+                 }
+ 
                  generateConstructor(clb);
  
!                 generateClassInitializationMethod(clb);
! 
  
                  // Forward the SAM method
                  clb.withMethodBody(interfaceMethodName,
                          methodDesc(interfaceMethodType),
                          ACC_PUBLIC,

*** 341,45 ***
  
                  if (isSerializable)
                      generateSerializationFriendlyMethods(clb);
                  else if (finalAccidentallySerializable)
                      generateSerializationHostileMethods(clb);
              }
          });
  
          // Define the generated class in this VM.
  
          try {
              // this class is linked at the indy callsite; so define a hidden nestmate
!             var classdata = useImplMethodHandle? implementation : null;
              return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK)
                           .defineClass(!disableEagerInitialization, classdata);
  
          } catch (Throwable t) {
              throw new InternalError(t);
          }
      }
  
!     /**
!      * Generate a static field and a static initializer that sets this field to an instance of the lambda
!      */
!     private void generateClassInitializer(ClassBuilder clb) {
!         ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType());
- 
-         // Generate the static final field that holds the lambda singleton
-         clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
- 
-         // Instantiate the lambda and store it to the static final field
-         clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() {
              @Override
              public void accept(CodeBuilder cob) {
!                 assert factoryType.parameterCount() == 0;
!                 cob.new_(lambdaClassEntry)
!                    .dup()
!                    .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc)))
!                    .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor)))
!                    .return_();
              }
          });
      }
  
      /**
--- 361,79 ---
  
                  if (isSerializable)
                      generateSerializationFriendlyMethods(clb);
                  else if (finalAccidentallySerializable)
                      generateSerializationHostileMethods(clb);
+ 
+                 if (reflectableLambdaInfo != null) {
+                     generateQuotedMethod(clb);
+                 }
              }
          });
  
          // Define the generated class in this VM.
  
          try {
              // this class is linked at the indy callsite; so define a hidden nestmate
!             List<?> classdata;
+             if (useImplMethodHandle || reflectableLambdaInfo != null) {
+                 classdata = reflectableLambdaInfo == null ?
+                         List.of(implementation) :
+                         List.of(implementation, reflectableLambdaInfo.opHandle(), reflectableLambdaInfo.extractOpHandle());
+             } else {
+                 classdata = null;
+             }
              return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK)
                           .defineClass(!disableEagerInitialization, classdata);
  
          } catch (Throwable t) {
              throw new InternalError(t);
          }
      }
  
!     private void generateClassInitializationMethod(ClassBuilder clb) {
!         if (!(factoryType.parameterCount() == 0 && disableEagerInitialization) && reflectableLambdaInfo == null) {
!             return;
!         }
!         clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<CodeBuilder>() {
              @Override
              public void accept(CodeBuilder cob) {
!                 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
!                     ClassDesc lambdaTypeDescriptor = classDesc(factoryType.returnType());
!                     // Generate the static final field that holds the lambda singleton
!                     clb.withField(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
!                     cob.new_(lambdaClassEntry)
!                             .dup()
+                             .invokespecial(pool.methodRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(INIT_NAME, constructorTypeDesc)))
+                             .putstatic(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor)));
+                 }
+ 
+                 if (reflectableLambdaInfo != null) {
+                     // if we visit a callsite twice, we will use the same class
+                     // if the lambda doesn't capture values we only have one instance, model shared anyway
+                     // if it captures values, each visit result in a creation of new instance of the class
+                     // those instances have the same code model generated by the compiler
+                     // they may differ in captured values
+                     // as first step let's share the compiler generated code model
+                     ClassDesc funcOpClassDesc = reflectableLambdaInfo.funcOpClass();
+                     clb.withField(COMPILER_GENERATED_MODEL_FIELD_NAME, funcOpClassDesc,
+                             ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
+ 
+                     ConstantPoolBuilder cp = pool;
+                     MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT);
+                     NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle);
+                     // load quotableOpGetter
+                     cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH));
+                     MethodType mtype = quotableOpGetterInfo.getMethodType();
+                     if (quotableOpGetterInfo.getReferenceKind() != MethodHandleInfo.REF_invokeStatic) {
+                         mtype = mtype.insertParameterTypes(0, implClass);
+                     }
+                     cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get());
+                     cob.checkcast(funcOpClassDesc);
+                     cob.putstatic(lambdaClassEntry.asSymbol(), COMPILER_GENERATED_MODEL_FIELD_NAME, funcOpClassDesc);
+                 }
+                 cob.return_();
              }
          });
      }
  
      /**

*** 397,15 ***
--- 451,51 ---
                          for (int i = 0; i < parameterCount; i++) {
                              cob.aload(0)
                                 .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i))
                                 .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i])));
                          }
+                         if (reflectableLambdaInfo != null) {
+                             generateQuotedFieldInitializer(cob);
+                         }
                          cob.return_();
                      }
                  });
      }
  
+     private void generateQuotedFieldInitializer(CodeBuilder cob) {
+         ConstantPoolBuilder cp = cob.constantPool();
+         MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT);
+         NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle);
+         // push the receiver on the stack for operand of put field instruction
+         cob.aload(0)
+                 // load class data: CodeReflectionSupport.HANDLE_MAKE_QUOTED
+                 .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH))
+                 .getstatic(lambdaClassEntry.asSymbol(), COMPILER_GENERATED_MODEL_FIELD_NAME,
+                         reflectableLambdaInfo.funcOpClass());
+ 
+ 
+         // load captured args in array
+ 
+         int capturedArity = factoryType.parameterCount();
+         cob.loadConstant(capturedArity)
+            .anewarray(CD_Object);
+         // initialize quoted captures
+         for (int i = 0; i < capturedArity; i++) {
+             cob.dup()
+                .loadConstant(i)
+                .aload(0)
+                .getfield(lambdaClassEntry.asSymbol(), argName(i), argDescs[i]);
+             TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i]));
+             cob.aastore();
+         }
+ 
+         // Create a Quoted from FuncOp and captured args Object[]
+ 
+         cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(reflectableLambdaInfo.extractOpHandle().type()))
+            .putfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, reflectableLambdaInfo.quotedClass());
+     }
+ 
      private static class SerializationSupport {
          // Serialization support
          private static final ClassDesc CD_SerializedLambda = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;");
          private static final ClassDesc CD_ObjectOutputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectOutputStream;");
          private static final ClassDesc CD_ObjectInputStream = ClassOrInterfaceDescImpl.ofValidated("Ljava/io/ObjectInputStream;");

*** 458,10 ***
--- 548,24 ---
                             .areturn();
                      }
                  });
      }
  
+     /**
+     * Generate method #quoted()
+      */
+     private void generateQuotedMethod(ClassBuilder clb) {
+         clb.withMethod(NAME_METHOD_QUOTED, MethodTypeDesc.of(reflectableLambdaInfo.quotedClass()), ACC_PRIVATE + ACC_FINAL, new MethodBody(new Consumer<CodeBuilder>() {
+             @Override
+             public void accept(CodeBuilder cob) {
+                 cob.aload(0)
+                    .getfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, reflectableLambdaInfo.quotedClass())
+                    .areturn();
+             }
+         }));
+     }
+ 
      /**
       * Generate a readObject/writeObject method that is hostile to serialization
       */
      private void generateSerializationHostileMethods(ClassBuilder clb) {
          var hostileMethod = new Consumer<MethodBuilder>() {

*** 501,11 ***
                      cob.new_(implMethodClassDesc)
                         .dup();
                  }
                  if (useImplMethodHandle) {
                      ConstantPoolBuilder cp = cob.constantPool();
!                     cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()),
                                                      cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle)));
                  }
                  for (int i = 0; i < argDescs.length; i++) {
                      cob.aload(0)
                         .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i])));
--- 605,11 ---
                      cob.new_(implMethodClassDesc)
                         .dup();
                  }
                  if (useImplMethodHandle) {
                      ConstantPoolBuilder cp = cob.constantPool();
!                     cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA_AT), List.of(cp.intEntry(0))),
                                                      cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle)));
                  }
                  for (int i = 0; i < argDescs.length; i++) {
                      cob.aload(0)
                         .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i])));
< prev index next >