< prev index next >

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

Print this page
@@ -23,22 +23,26 @@
   * 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;
  
  import java.io.Serializable;
  import java.lang.classfile.ClassBuilder;
  import java.lang.classfile.ClassFile;
  import java.lang.classfile.CodeBuilder;
+ import java.lang.classfile.Label;
  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 +71,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 QUOTED_FIELD_NAME = "quoted";
+     private static final String MODEL_FIELD_NAME = "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 +156,16 @@
                                         MethodType interfaceMethodType,
                                         MethodHandle implementation,
                                         MethodType dynamicMethodType,
                                         boolean isSerializable,
                                         Class<?>[] altInterfaces,
-                                        MethodType[] altMethods)
+                                        MethodType[] altMethods,
+                                        ReflectableLambdaInfo reflectableLambdaInfo)
              throws LambdaConversionException {
          super(caller, factoryType, interfaceMethodName, interfaceMethodType,
                implementation, dynamicMethodType,
-               isSerializable, altInterfaces, altMethods);
+               isSerializable, altInterfaces, altMethods, reflectableLambdaInfo);
          implMethodClassDesc = implClassDesc(implClass);
          implMethodName = implInfo.getName();
          implMethodDesc = methodDesc(implInfo.getMethodType());
          constructorType = factoryType.changeReturnType(Void.TYPE);
          lambdaClassName = lambdaClassName(targetClass);

@@ -315,15 +332,22 @@
                  // 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 (reflectableLambdaInfo != null) {
+                     // the field that will hold the quoted instance
+                     clb.withField(QUOTED_FIELD_NAME, reflectableLambdaInfo.quotedClass(), ACC_PRIVATE);
+                     // the field that will hold the model
+                     clb.withField(MODEL_FIELD_NAME, reflectableLambdaInfo.funcOpClass(),
+                             ACC_PRIVATE | ACC_STATIC);
+                 }
+ 
                  generateConstructor(clb);
  
-                 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
-                     generateClassInitializer(clb);
-                 }
+                 generateClassInitializationMethod(clb);
+ 
  
                  // Forward the SAM method
                  clb.withMethodBody(interfaceMethodName,
                          methodDesc(interfaceMethodType),
                          ACC_PUBLIC,

@@ -341,45 +365,54 @@
  
                  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
-             var classdata = useImplMethodHandle? implementation : null;
+             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);
          }
      }
  
-     /**
-      * 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<>() {
+     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) {
-                 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_();
+                 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)));
+                 }
+                 cob.return_();
              }
          });
      }
  
      /**

@@ -458,10 +491,114 @@
                             .areturn();
                      }
                  });
      }
  
+     /**
+     * Generate method #__internal_quoted()
+      */
+     private void generateQuotedMethod(ClassBuilder clb) {
+         clb.withMethod(NAME_METHOD_QUOTED, MethodTypeDesc.of(reflectableLambdaInfo.quotedClass()), ACC_PRIVATE, new MethodBody(new Consumer<CodeBuilder>() {
+             @Override
+             public void accept(CodeBuilder cob) {
+                 cob.aload(0)
+                         .invokevirtual(lambdaClassEntry.asSymbol(), "getQuoted", MethodTypeDesc.of(reflectableLambdaInfo.quotedClass()))
+                         .areturn();
+             }
+         }));
+         // generate helper methods
+         /*
+         synchronized Quoted getQuoted() {
+             Quoted v = quoted;
+             if (v == null) {
+                 v = quoted = Quoted.extractOp(getModel(), captures);
+             }
+             return v;
+         }
+         * */
+         clb.withMethod("getQuoted", MethodTypeDesc.of(reflectableLambdaInfo.quotedClass()),
+                 ACC_PRIVATE + ACC_SYNCHRONIZED,
+                 new MethodBody(new Consumer<CodeBuilder>() {
+                     @Override
+                     public void accept(CodeBuilder cob) {
+                         cob.aload(0)
+                                 .getfield(lambdaClassEntry.asSymbol(), QUOTED_FIELD_NAME, reflectableLambdaInfo.quotedClass())
+                                 .astore(1)
+                                 .aload(1)
+                                 .ifThen(Opcode.IFNULL, bcb -> {
+                                     bcb.aload(0); // will be used by putfield
+ 
+                                     // load class data: MH to Quoted.extractOp
+                                     ConstantPoolBuilder cp = bcb.constantPool();
+                                     MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT);
+                                     NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle);
+                                     bcb.ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH));
+ 
+                                     bcb.invokestatic(lambdaClassEntry.asSymbol(), "getModel", MethodTypeDesc.of(reflectableLambdaInfo.funcOpClass()));
+ 
+                                     // load captured args in array
+                                     int capturedArity = factoryType.parameterCount();
+                                     bcb.loadConstant(capturedArity)
+                                             .anewarray(CD_Object);
+                                     for (int i = 0; i < capturedArity; i++) {
+                                         bcb.dup()
+                                                 .loadConstant(i)
+                                                 .aload(0)
+                                                 .getfield(lambdaClassEntry.asSymbol(), argName(i), argDescs[i]);
+                                         TypeConvertingMethodAdapter.boxIfTypePrimitive(bcb, TypeKind.from(argDescs[i]));
+                                         bcb.aastore();
+                                     }
+ 
+                                     // invoke Quoted.extractOp
+                                     bcb.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(reflectableLambdaInfo.extractOpHandle().type()))
+                                             .dup_x1()
+                                             .putfield(lambdaClassEntry.asSymbol(), QUOTED_FIELD_NAME, reflectableLambdaInfo.quotedClass())
+                                             .astore(1);
+ 
+                                 })
+                                 .aload(1)
+                                 .areturn();
+                     }
+                 }));
+ 
+         /*
+         private static synchronized CoreOp.FuncOp getModel() {
+             FuncOp v = model;
+             if (v == null) {
+                 v = model = ...invoke lambda op building method...
+             }
+             return v;
+         }
+         * */
+         clb.withMethod("getModel", MethodTypeDesc.of(reflectableLambdaInfo.funcOpClass()),
+                 ACC_PRIVATE + ACC_STATIC + ACC_SYNCHRONIZED,
+                 new MethodBody(new Consumer<CodeBuilder>() {
+                     @Override
+                     public void accept(CodeBuilder cob) {
+                         ClassDesc funcOpClassDesc = reflectableLambdaInfo.funcOpClass();
+                         cob.getstatic(lambdaClassEntry.asSymbol(), MODEL_FIELD_NAME, funcOpClassDesc)
+                                 .astore(0)
+                                 .aload(0)
+                                 .ifThen(Opcode.IFNULL, bcb -> {
+                                     // load class data: MH to op building method
+                                     ConstantPoolBuilder cp = pool;
+                                     MethodHandleEntry bsmDataAt = cp.methodHandleEntry(BSM_CLASS_DATA_AT);
+                                     NameAndTypeEntry natMH = cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle);
+                                     cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH));
+                                     MethodType mtype = quotableOpGetterInfo.getMethodType();
+                                     cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get())
+                                             .checkcast(funcOpClassDesc)
+                                             .dup()
+                                             .putstatic(lambdaClassEntry.asSymbol(), MODEL_FIELD_NAME, funcOpClassDesc)
+                                             .astore(0);
+                                 })
+                                 .aload(0)
+                                 .areturn();
+                     }
+                 }));
+     }
+ 
      /**
       * Generate a readObject/writeObject method that is hostile to serialization
       */
      private void generateSerializationHostileMethods(ClassBuilder clb) {
          var hostileMethod = new Consumer<MethodBuilder>() {

@@ -501,11 +638,11 @@
                      cob.new_(implMethodClassDesc)
                         .dup();
                  }
                  if (useImplMethodHandle) {
                      ConstantPoolBuilder cp = cob.constantPool();
-                     cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()),
+                     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 >