< prev index next >

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

Print this page
@@ -36,16 +36,23 @@
  import java.lang.classfile.CodeBuilder;
  import java.lang.classfile.FieldBuilder;
  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.DynamicConstantDesc;
  import java.lang.constant.MethodTypeDesc;
+ import java.lang.invoke.MethodHandles.Lookup;
+ import java.lang.module.Configuration;
+ import java.lang.module.ModuleFinder;
  import java.lang.reflect.Modifier;
  import java.util.LinkedHashSet;
  import java.util.List;
+ import java.util.Optional;
+ import java.util.ServiceLoader;
  import java.util.Set;
  import java.util.function.Consumer;
  
  import static java.lang.classfile.ClassFile.*;
  import java.lang.classfile.attribute.ExceptionsAttribute;

@@ -72,15 +79,26 @@
  /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
      private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
      private static final String[] EMPTY_STRING_ARRAY = new String[0];
      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 = "quoted";
+     private static final String quotedInstanceFieldName = "quoted";
+ 
      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";

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

@@ -314,10 +333,15 @@
                  // Generate final fields to be filled in by constructor
                  for (int i = 0; i < argDescs.length; i++) {
                      clb.withField(argNames[i], argDescs[i], ACC_PRIVATE | ACC_FINAL);
                  }
  
+                 // if quotable, generate the field that will hold the value of quoted
+                 if (quotableOpField != null) {
+                     clb.withField(quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted, ACC_PRIVATE + ACC_FINAL);
+                 }
+ 
                  generateConstructor(clb);
  
                  if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
                      generateClassInitializer(clb);
                  }

@@ -340,18 +364,29 @@
  
                  if (isSerializable)
                      generateSerializationFriendlyMethods(clb);
                  else if (finalAccidentallySerializable)
                      generateSerializationHostileMethods(clb);
+ 
+                 if (quotableOpField != null) {
+                     generateQuotableMethod(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 || quotableOpField != null) {
+                 classdata = quotableOpField == null ?
+                         List.of(implementation) :
+                         List.of(implementation, quotableOpField, CodeReflectionSupport.HANDLE_MAKE_QUOTED);
+             } else {
+                 classdata = null;
+             }
              return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, lambdaProxyClassFileDumper, NESTMATE_CLASS | STRONG_LOADER_LINK)
                           .defineClass(!disableEagerInitialization, classdata);
  
          } catch (Throwable t) {
              throw new InternalError(t);

@@ -397,15 +432,85 @@
                              cob.aload(0);
                              Class<?> argType = factoryType.parameterType(i);
                              cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i));
                              cob.putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i])));
                          }
+                         if (quotableOpField != 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)
+            .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(2))), natMH))
+         // load op string from field
+            .ldc(cp.constantDynamicEntry(cp.bsmEntry(bsmDataAt, List.of(cp.intEntry(1))), natMH));
+         MethodType mtype = quotableOpFieldInfo.getMethodType();
+         if (quotableOpFieldInfo.getReferenceKind() != MethodHandleInfo.REF_getStatic) {
+             mtype = mtype.insertParameterTypes(0, implClass);
+         }
+         cob.invokevirtual(CD_MethodHandle, "invokeExact", mtype.describeConstable().get());
+ 
+         // 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(), argNames[i], argDescs[i]);
+             TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i]));
+             cob.aastore();
+         }
+ 
+         // now create a Quoted from String and captured args Object[]
+ 
+         cob.invokevirtual(CD_MethodHandle, "invokeExact", methodDesc(CodeReflectionSupport.HANDLE_MAKE_QUOTED.type()))
+            .putfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted);
+     }
+ 
+     static class CodeReflectionSupport {
+         static final Class<?> QUOTED_CLASS;
+         static final Class<?> QUOTABLE_CLASS;
+         static final MethodHandle HANDLE_MAKE_QUOTED;
+ 
+         static {
+             try {
+                 // we know jdk.incubator code is there, no need to check
+                 ModuleLayer parent = ModuleLayer.boot();
+                 Configuration cf = parent.configuration()
+                         .resolve(ModuleFinder.of(), ModuleFinder.ofSystem(), Set.of("jdk.incubator.code"));
+                 ClassLoader scl = ClassLoader.getSystemClassLoader();
+                 ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl);
+                 QUOTED_CLASS = layer.findLoader("jdk.incubator.code")
+                         .loadClass("jdk.incubator.code.Quoted");
+                 QUOTABLE_CLASS = layer.findLoader("jdk.incubator.code")
+                         .loadClass("jdk.incubator.code.Quotable");
+                 Class<?> quotedHelper = layer.findLoader("jdk.incubator.code")
+                         .loadClass("jdk.incubator.code.internal.QuotedHelper");
+                 MethodHandle makeQuoted = Lookup.IMPL_LOOKUP.findStatic(quotedHelper, "makeQuoted",
+                         MethodType.methodType(QUOTED_CLASS, MethodHandles.Lookup.class, String.class, Object[].class));
+                 HANDLE_MAKE_QUOTED = makeQuoted.bindTo(Lookup.IMPL_LOOKUP);
+             } catch (Throwable ex) {
+                 throw new ExceptionInInitializerError(ex);
+             }
+         }
+ 
+         static final ClassDesc CD_Quoted = QUOTED_CLASS.describeConstable().get();
+         static final MethodTypeDesc MTD_Quoted = MethodTypeDescImpl.ofValidated(CD_Quoted);
+     }
+ 
      private static class SerializationSupport {
          // Serialization support
          private static final ClassDesc CD_SerializedLambda = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/SerializedLambda;");
          private static final ClassDesc CD_ObjectOutputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectOutputStream;");
          private static final ClassDesc CD_ObjectInputStream = ReferenceClassDescImpl.ofValidated("Ljava/io/ObjectInputStream;");

@@ -458,10 +563,24 @@
                             .areturn();
                      }
                  });
      }
  
+     /**
+      * Generate a writeReplace method that supports serialization
+      */
+     private void generateQuotableMethod(ClassBuilder clb) {
+         clb.withMethod(NAME_METHOD_QUOTED, CodeReflectionSupport.MTD_Quoted, ACC_PUBLIC + ACC_FINAL, new MethodBody(new Consumer<CodeBuilder>() {
+             @Override
+             public void accept(CodeBuilder cob) {
+                 cob.aload(0)
+                    .getfield(lambdaClassEntry.asSymbol(), quotedInstanceFieldName, CodeReflectionSupport.CD_Quoted)
+                    .areturn();
+             }
+         }));
+     }
+ 
      /**
       * Generate a readObject/writeObject method that is hostile to serialization
       */
      private void generateSerializationHostileMethods(ClassBuilder clb) {
          var hostileMethod = new Consumer<MethodBuilder>() {

@@ -501,14 +620,14 @@
                      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 < argNames.length; i++) {
+                 for (int i = 0; i < argNames.length ; i++) {
                      cob.aload(0)
                         .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i])));
                  }
  
                  convertArgumentTypes(cob, methodType);
< prev index next >