< prev index next > src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
Print this page
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;
/* 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";
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);
MethodType interfaceMethodType,
MethodHandle implementation,
MethodType dynamicMethodType,
boolean isSerializable,
Class<?>[] altInterfaces,
! MethodType[] altMethods,
+ MethodHandle reflectiveField)
throws LambdaConversionException {
super(caller, factoryType, interfaceMethodName, interfaceMethodType,
implementation, dynamicMethodType,
! isSerializable, altInterfaces, altMethods, reflectiveField);
implMethodClassDesc = implClassDesc(implClass);
implMethodName = implInfo.getName();
implMethodDesc = methodDesc(implInfo.getMethodType());
constructorType = factoryType.changeReturnType(Void.TYPE);
lambdaClassName = lambdaClassName(targetClass);
// 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);
}
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);
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
! 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);
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;");
.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>() {
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 < argNames.length; i++) {
cob.aload(0)
.getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i])));
}
convertArgumentTypes(cob, methodType);
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 < argNames.length ; i++) {
cob.aload(0)
.getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i])));
}
convertArgumentTypes(cob, methodType);
< prev index next >