17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import jdk.internal.misc.CDS;
29 import jdk.internal.org.objectweb.asm.*;
30 import jdk.internal.util.ClassFileDumper;
31 import sun.invoke.util.BytecodeDescriptor;
32 import sun.invoke.util.VerifyAccess;
33 import sun.security.action.GetBooleanAction;
34
35 import java.io.Serializable;
36 import java.lang.constant.ConstantDescs;
37 import java.lang.reflect.Modifier;
38 import java.util.LinkedHashSet;
39 import java.util.Set;
40
41 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
42 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
43 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
44 import static java.lang.invoke.MethodType.methodType;
45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
46
47 /**
48 * Lambda metafactory implementation which dynamically creates an
49 * inner-class-like class per lambda callsite.
50 *
51 * @see LambdaMetafactory
52 */
53 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
54 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
55 private static final String JAVA_LANG_OBJECT = "java/lang/Object";
56 private static final String NAME_CTOR = "<init>";
57 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
58
59 //Serialization support
60 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
61 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
62 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
63 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
64 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
65
66 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
67 private static final String NAME_METHOD_READ_OBJECT = "readObject";
68 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
69
70 private static final String DESCR_CLASS = "Ljava/lang/Class;";
71 private static final String DESCR_STRING = "Ljava/lang/String;";
72 private static final String DESCR_OBJECT = "Ljava/lang/Object;";
73 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
74 = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
75 + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
76
77 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
78 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
79
80 private static final String[] EMPTY_STRING_ARRAY = new String[0];
81
82 // For dumping generated classes to disk, for debugging purposes
83 private static final ClassFileDumper lambdaProxyClassFileDumper;
84
85 private static final boolean disableEagerInitialization;
86
87 // condy to load implMethod from class data
88 private static final ConstantDynamic implMethodCondy;
89
90 static {
91 // To dump the lambda proxy classes, set this system property:
92 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
93 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
94 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles";
95 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES");
96
97 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
98 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
99
100 // condy to load implMethod from class data
101 MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
102 Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
103 classDataMType.descriptorString(), false);
104 implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
105 }
106
107 // See context values in AbstractValidatingLambdaMetafactory
108 private final String implMethodClassName; // Name of type containing implementation "CC"
109 private final String implMethodName; // Name of implementation method "impl"
110 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
111 private final MethodType constructorType; // Generated class constructor type "(CC)void"
112 private final ClassWriter cw; // ASM class writer
113 private final String[] argNames; // Generated names for the constructor arguments
114 private final String[] argDescs; // Type descriptors for the constructor arguments
115 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda"
116 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
117
118 /**
119 * General meta-factory constructor, supporting both standard cases and
120 * allowing for uncommon options such as serialization or bridging.
121 *
122 * @param caller Stacked automatically by VM; represents a lookup context
123 * with the accessibility privileges of the caller.
124 * @param factoryType Stacked automatically by VM; the signature of the
132 * which the lambda or method reference is being
133 * converted, represented as a String.
134 * @param interfaceMethodType Type of the method in the functional interface to
135 * which the lambda or method reference is being
136 * converted, represented as a MethodType.
137 * @param implementation The implementation method which should be called (with
138 * suitable adaptation of argument types, return types,
139 * and adjustment for captured arguments) when methods of
140 * the resulting functional interface instance are invoked.
141 * @param dynamicMethodType The signature of the primary functional
142 * interface method after type variables are
143 * substituted with their instantiation from
144 * the capture site
145 * @param isSerializable Should the lambda be made serializable? If set,
146 * either the target type or one of the additional SAM
147 * types must extend {@code Serializable}.
148 * @param altInterfaces Additional interfaces which the lambda object
149 * should implement.
150 * @param altMethods Method types for additional signatures to be
151 * implemented by invoking the implementation method
152 * @throws LambdaConversionException If any of the meta-factory protocol
153 * invariants are violated
154 * @throws SecurityException If a security manager is present, and it
155 * <a href="MethodHandles.Lookup.html#secmgr">denies access</a>
156 * from {@code caller} to the package of {@code implementation}.
157 */
158 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
159 MethodType factoryType,
160 String interfaceMethodName,
161 MethodType interfaceMethodType,
162 MethodHandle implementation,
163 MethodType dynamicMethodType,
164 boolean isSerializable,
165 Class<?>[] altInterfaces,
166 MethodType[] altMethods)
167 throws LambdaConversionException {
168 super(caller, factoryType, interfaceMethodName, interfaceMethodType,
169 implementation, dynamicMethodType,
170 isSerializable, altInterfaces, altMethods);
171 implMethodClassName = implClass.getName().replace('.', '/');
172 implMethodName = implInfo.getName();
173 implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
174 constructorType = factoryType.changeReturnType(Void.TYPE);
175 lambdaClassName = lambdaClassName(targetClass);
176 // If the target class invokes a protected method inherited from a
177 // superclass in a different package, or does 'invokespecial', the
178 // lambda class has no access to the resolved method, or does
179 // 'invokestatic' on a hidden class which cannot be resolved by name.
180 // Instead, we need to pass the live implementation method handle to
181 // the proxy class to invoke directly. (javac prefers to avoid this
182 // situation by generating bridges in the target class)
183 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) &&
184 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
185 implKind == H_INVOKESPECIAL ||
186 implKind == H_INVOKESTATIC && implClass.isHidden();
187 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
188 int parameterCount = factoryType.parameterCount();
189 if (parameterCount > 0) {
190 argNames = new String[parameterCount];
312 for (Class<?> i : altInterfaces) {
313 itfs.add(i.getName().replace('.', '/'));
314 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
315 }
316 interfaceNames = itfs.toArray(new String[itfs.size()]);
317 }
318
319 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
320 lambdaClassName, null,
321 JAVA_LANG_OBJECT, interfaceNames);
322
323 // Generate final fields to be filled in by constructor
324 for (int i = 0; i < argDescs.length; i++) {
325 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
326 argNames[i],
327 argDescs[i],
328 null, null);
329 fv.visitEnd();
330 }
331
332 generateConstructor();
333
334 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
335 generateClassInitializer();
336 }
337
338 // Forward the SAM method
339 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
340 interfaceMethodType.toMethodDescriptorString(), null, null);
341 new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
342
343 // Forward the altMethods
344 if (altMethods != null) {
345 for (MethodType mt : altMethods) {
346 mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
347 mt.toMethodDescriptorString(), null, null);
348 new ForwardingMethodGenerator(mv).generate(mt);
349 }
350 }
351
352 if (isSerializable)
353 generateSerializationFriendlyMethods();
354 else if (accidentallySerializable)
355 generateSerializationHostileMethods();
356
357 cw.visitEnd();
358
359 // Define the generated class in this VM.
360
361 final byte[] classBytes = cw.toByteArray();
362 try {
363 // this class is linked at the indy callsite; so define a hidden nestmate
364 var classdata = useImplMethodHandle? implementation : null;
365 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
366 .defineClass(!disableEagerInitialization, classdata);
367
368 } catch (Throwable t) {
369 throw new InternalError(t);
370 }
371 }
372
373 /**
374 * Generate a static field and a static initializer that sets this field to an instance of the lambda
375 */
376 private void generateClassInitializer() {
377 String lambdaTypeDescriptor = factoryType.returnType().descriptorString();
378
379 // Generate the static final field that holds the lambda singleton
380 FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
381 LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null);
382 fv.visitEnd();
383
384 // Instantiate the lambda and store it to the static final field
398
399 /**
400 * Generate the constructor for the class
401 */
402 private void generateConstructor() {
403 // Generate constructor
404 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
405 constructorType.toMethodDescriptorString(), null, null);
406 ctor.visitCode();
407 ctor.visitVarInsn(ALOAD, 0);
408 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
409 METHOD_DESCRIPTOR_VOID, false);
410 int parameterCount = factoryType.parameterCount();
411 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
412 ctor.visitVarInsn(ALOAD, 0);
413 Class<?> argType = factoryType.parameterType(i);
414 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
415 lvIndex += getParameterSize(argType);
416 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
417 }
418 ctor.visitInsn(RETURN);
419 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
420 ctor.visitMaxs(-1, -1);
421 ctor.visitEnd();
422 }
423
424 /**
425 * Generate a writeReplace method that supports serialization
426 */
427 private void generateSerializationFriendlyMethods() {
428 TypeConvertingMethodAdapter mv
429 = new TypeConvertingMethodAdapter(
430 cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
431 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
432 null, null));
433
434 mv.visitCode();
435 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
436 mv.visitInsn(DUP);
437 mv.visitLdcInsn(Type.getType(targetClass));
438 mv.visitLdcInsn(factoryType.returnType().getName().replace('.', '/'));
439 mv.visitLdcInsn(interfaceMethodName);
440 mv.visitLdcInsn(interfaceMethodType.toMethodDescriptorString());
441 mv.visitLdcInsn(implInfo.getReferenceKind());
442 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
443 mv.visitLdcInsn(implInfo.getName());
444 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
445 mv.visitLdcInsn(dynamicMethodType.toMethodDescriptorString());
446 mv.iconst(argDescs.length);
447 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
448 for (int i = 0; i < argDescs.length; i++) {
449 mv.visitInsn(DUP);
450 mv.iconst(i);
451 mv.visitVarInsn(ALOAD, 0);
452 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
453 mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
454 mv.visitInsn(AASTORE);
455 }
456 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
457 DESCR_CTOR_SERIALIZED_LAMBDA, false);
458 mv.visitInsn(ARETURN);
459 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
460 mv.visitMaxs(-1, -1);
461 mv.visitEnd();
462 }
463
464 /**
465 * Generate a readObject/writeObject method that is hostile to serialization
466 */
467 private void generateSerializationHostileMethods() {
468 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
469 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
470 null, SER_HOSTILE_EXCEPTIONS);
471 mv.visitCode();
472 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
473 mv.visitInsn(DUP);
474 mv.visitLdcInsn("Non-serializable lambda");
475 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
476 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
477 mv.visitInsn(ATHROW);
478 mv.visitMaxs(-1, -1);
479 mv.visitEnd();
480
481 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
482 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
483 null, SER_HOSTILE_EXCEPTIONS);
495 /**
496 * This class generates a method body which calls the lambda implementation
497 * method, converting arguments, as needed.
498 */
499 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
500
501 ForwardingMethodGenerator(MethodVisitor mv) {
502 super(mv);
503 }
504
505 void generate(MethodType methodType) {
506 visitCode();
507
508 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
509 visitTypeInsn(NEW, implMethodClassName);
510 visitInsn(DUP);
511 }
512 if (useImplMethodHandle) {
513 visitLdcInsn(implMethodCondy);
514 }
515 for (int i = 0; i < argNames.length; i++) {
516 visitVarInsn(ALOAD, 0);
517 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
518 }
519
520 convertArgumentTypes(methodType);
521
522 if (useImplMethodHandle) {
523 MethodType mtype = implInfo.getMethodType();
524 if (implKind != MethodHandleInfo.REF_invokeStatic) {
525 mtype = mtype.insertParameterTypes(0, implClass);
526 }
527 visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
528 "invokeExact", mtype.descriptorString(), false);
529 } else {
530 // Invoke the method we want to forward to
531 visitMethodInsn(invocationOpcode(), implMethodClassName,
532 implMethodName, implMethodDesc,
533 implClass.isInterface());
534 }
535 // Convert the return value (if any) and return it
536 // Note: if adapting from non-void to void, the 'return'
537 // instruction will pop the unneeded result
538 Class<?> implReturnClass = implMethodType.returnType();
539 Class<?> samReturnClass = methodType.returnType();
540 convertType(implReturnClass, samReturnClass, samReturnClass);
541 visitInsn(getReturnOpcode(samReturnClass));
542 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
543 visitMaxs(-1, -1);
544 visitEnd();
545 }
546
547 private void convertArgumentTypes(MethodType samType) {
548 int lvIndex = 0;
549 int samParametersLength = samType.parameterCount();
550 int captureArity = factoryType.parameterCount();
551 for (int i = 0; i < samParametersLength; i++) {
552 Class<?> argType = samType.parameterType(i);
553 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
554 lvIndex += getParameterSize(argType);
555 convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
556 }
557 }
558
559 private int invocationOpcode() throws InternalError {
560 return switch (implKind) {
561 case MethodHandleInfo.REF_invokeStatic -> INVOKESTATIC;
562 case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL;
563 case MethodHandleInfo.REF_invokeVirtual -> INVOKEVIRTUAL;
564 case MethodHandleInfo.REF_invokeInterface -> INVOKEINTERFACE;
565 case MethodHandleInfo.REF_invokeSpecial -> INVOKESPECIAL;
566 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
567 };
568 }
569 }
570
589 return RETURN;
590 }
591 return IRETURN + getOpcodeOffset(c);
592 }
593
594 private static int getOpcodeOffset(Class<?> c) {
595 if (c.isPrimitive()) {
596 if (c == Long.TYPE) {
597 return 1;
598 } else if (c == Float.TYPE) {
599 return 2;
600 } else if (c == Double.TYPE) {
601 return 3;
602 }
603 return 0;
604 } else {
605 return 4;
606 }
607 }
608
609 }
|
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import jdk.internal.misc.CDS;
29 import jdk.internal.org.objectweb.asm.*;
30 import jdk.internal.util.ClassFileDumper;
31 import sun.invoke.util.BytecodeDescriptor;
32 import sun.invoke.util.VerifyAccess;
33 import sun.security.action.GetBooleanAction;
34
35 import java.io.Serializable;
36 import java.lang.constant.ConstantDescs;
37 import java.lang.reflect.code.op.CoreOp.FuncOp;
38 import java.lang.reflect.code.Quoted;
39 import java.lang.reflect.code.interpreter.Interpreter;
40 import java.lang.reflect.code.parser.OpParser;
41 import java.lang.invoke.MethodHandles.Lookup;
42 import java.lang.reflect.Modifier;
43 import java.util.*;
44
45 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
46 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
47 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
48 import static java.lang.invoke.MethodType.methodType;
49 import static jdk.internal.org.objectweb.asm.Opcodes.*;
50
51 /**
52 * Lambda metafactory implementation which dynamically creates an
53 * inner-class-like class per lambda callsite.
54 *
55 * @see LambdaMetafactory
56 */
57 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
58 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
59 private static final String JAVA_LANG_OBJECT = "java/lang/Object";
60 private static final String NAME_CTOR = "<init>";
61 private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
62
63 //Serialization support
64 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
65 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
66 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
67 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
68 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
69 private static final String DESCR_METHOD_QUOTED = "()Ljava/lang/reflect/code/Quoted;";
70
71 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
72 private static final String NAME_METHOD_READ_OBJECT = "readObject";
73 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
74 private static final String NAME_METHOD_QUOTED = "quoted";
75
76 private static final String DESCR_CLASS = "Ljava/lang/Class;";
77 private static final String DESCR_STRING = "Ljava/lang/String;";
78 private static final String DESCR_OBJECT = "Ljava/lang/Object;";
79 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
80 = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
81 + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
82
83 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
84 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
85
86 private static final String[] EMPTY_STRING_ARRAY = new String[0];
87
88 // For dumping generated classes to disk, for debugging purposes
89 private static final ClassFileDumper lambdaProxyClassFileDumper;
90
91 private static final boolean disableEagerInitialization;
92
93 // condy to load implMethod from class data
94 private static final ConstantDynamic implMethodCondy;
95
96 // condy to load reflective field from class data
97 private static final ConstantDynamic reflectiveFieldCondy;
98
99 private static final ConstantDynamic makeQuotedMethodCondy;
100
101 private static final MethodHandle HANDLE_MAKE_QUOTED;
102
103 private static final String quotedInstanceFieldName = "quoted";
104 private static final String quotedInstanceFieldDesc = Quoted.class.descriptorString();
105
106
107 static {
108 // To dump the lambda proxy classes, set this system property:
109 // -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
110 // or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
111 final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles";
112 lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, "DUMP_LAMBDA_PROXY_CLASS_FILES");
113
114 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
115 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
116
117 // condy to load implMethod from class data
118 MethodType classDataAtMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class, int.class);
119 Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classDataAt",
120 classDataAtMType.descriptorString(), false);
121 implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm, 0);
122 reflectiveFieldCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm, 1);
123 makeQuotedMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm, 2);
124
125 try {
126 HANDLE_MAKE_QUOTED = MethodHandles.lookup().findStatic(
127 InnerClassLambdaMetafactory.class, "makeQuoted",
128 MethodType.methodType(Quoted.class, String.class, Object[].class));
129 } catch (Throwable ex) {
130 throw new AssertionError(ex);
131 }
132 }
133
134 // See context values in AbstractValidatingLambdaMetafactory
135 private final String implMethodClassName; // Name of type containing implementation "CC"
136 private final String implMethodName; // Name of implementation method "impl"
137 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
138 private final MethodType constructorType; // Generated class constructor type "(CC)void"
139 private final ClassWriter cw; // ASM class writer
140 private final String[] argNames; // Generated names for the constructor arguments
141 private final String[] argDescs; // Type descriptors for the constructor arguments
142 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda"
143 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
144
145 /**
146 * General meta-factory constructor, supporting both standard cases and
147 * allowing for uncommon options such as serialization or bridging.
148 *
149 * @param caller Stacked automatically by VM; represents a lookup context
150 * with the accessibility privileges of the caller.
151 * @param factoryType Stacked automatically by VM; the signature of the
159 * which the lambda or method reference is being
160 * converted, represented as a String.
161 * @param interfaceMethodType Type of the method in the functional interface to
162 * which the lambda or method reference is being
163 * converted, represented as a MethodType.
164 * @param implementation The implementation method which should be called (with
165 * suitable adaptation of argument types, return types,
166 * and adjustment for captured arguments) when methods of
167 * the resulting functional interface instance are invoked.
168 * @param dynamicMethodType The signature of the primary functional
169 * interface method after type variables are
170 * substituted with their instantiation from
171 * the capture site
172 * @param isSerializable Should the lambda be made serializable? If set,
173 * either the target type or one of the additional SAM
174 * types must extend {@code Serializable}.
175 * @param altInterfaces Additional interfaces which the lambda object
176 * should implement.
177 * @param altMethods Method types for additional signatures to be
178 * implemented by invoking the implementation method
179 * @param reflectiveField a {@linkplain MethodHandles.Lookup#findGetter(Class, String, Class) getter}
180 * method handle that is used to retrieve the string representation of the
181 * quotable lambda's associated intermediate representation.
182 * @throws LambdaConversionException If any of the meta-factory protocol
183 * invariants are violated
184 * @throws SecurityException If a security manager is present, and it
185 * <a href="MethodHandles.Lookup.html#secmgr">denies access</a>
186 * from {@code caller} to the package of {@code implementation}.
187 */
188 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
189 MethodType factoryType,
190 String interfaceMethodName,
191 MethodType interfaceMethodType,
192 MethodHandle implementation,
193 MethodType dynamicMethodType,
194 boolean isSerializable,
195 Class<?>[] altInterfaces,
196 MethodType[] altMethods,
197 MethodHandle reflectiveField)
198 throws LambdaConversionException {
199 super(caller, factoryType, interfaceMethodName, interfaceMethodType,
200 implementation, dynamicMethodType,
201 isSerializable, altInterfaces, altMethods, reflectiveField);
202 implMethodClassName = implClass.getName().replace('.', '/');
203 implMethodName = implInfo.getName();
204 implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
205 constructorType = factoryType.changeReturnType(Void.TYPE);
206 lambdaClassName = lambdaClassName(targetClass);
207 // If the target class invokes a protected method inherited from a
208 // superclass in a different package, or does 'invokespecial', the
209 // lambda class has no access to the resolved method, or does
210 // 'invokestatic' on a hidden class which cannot be resolved by name.
211 // Instead, we need to pass the live implementation method handle to
212 // the proxy class to invoke directly. (javac prefers to avoid this
213 // situation by generating bridges in the target class)
214 useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) &&
215 !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
216 implKind == H_INVOKESPECIAL ||
217 implKind == H_INVOKESTATIC && implClass.isHidden();
218 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
219 int parameterCount = factoryType.parameterCount();
220 if (parameterCount > 0) {
221 argNames = new String[parameterCount];
343 for (Class<?> i : altInterfaces) {
344 itfs.add(i.getName().replace('.', '/'));
345 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
346 }
347 interfaceNames = itfs.toArray(new String[itfs.size()]);
348 }
349
350 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
351 lambdaClassName, null,
352 JAVA_LANG_OBJECT, interfaceNames);
353
354 // Generate final fields to be filled in by constructor
355 for (int i = 0; i < argDescs.length; i++) {
356 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
357 argNames[i],
358 argDescs[i],
359 null, null);
360 fv.visitEnd();
361 }
362
363 // if quotable, generate the field that will hold the value of quoted
364 if (quotableOpField != null) {
365 cw.visitField(ACC_PRIVATE + ACC_FINAL,
366 quotedInstanceFieldName,
367 quotedInstanceFieldDesc,
368 null, null);
369 }
370
371 generateConstructor();
372
373 if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
374 generateClassInitializer();
375 }
376
377 // Forward the SAM method
378 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
379 interfaceMethodType.toMethodDescriptorString(), null, null);
380 new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
381
382 // Forward the altMethods
383 if (altMethods != null) {
384 for (MethodType mt : altMethods) {
385 mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
386 mt.toMethodDescriptorString(), null, null);
387 new ForwardingMethodGenerator(mv).generate(mt);
388 }
389 }
390
391 if (isSerializable)
392 generateSerializationFriendlyMethods();
393 else if (accidentallySerializable)
394 generateSerializationHostileMethods();
395
396 if (quotableOpField != null) {
397 generateQuotableMethod();
398 }
399
400 cw.visitEnd();
401
402 // Define the generated class in this VM.
403
404 final byte[] classBytes = cw.toByteArray();
405 try {
406 // this class is linked at the indy callsite; so define a hidden nestmate
407 List<?> classdata;
408 if (useImplMethodHandle || quotableOpField != null) {
409 classdata = quotableOpField == null ?
410 List.of(implementation) :
411 List.of(implementation, quotableOpField, HANDLE_MAKE_QUOTED);
412 } else {
413 classdata = null;
414 }
415 return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
416 .defineClass(!disableEagerInitialization, classdata);
417
418 } catch (Throwable t) {
419 throw new InternalError(t);
420 }
421 }
422
423 /**
424 * Generate a static field and a static initializer that sets this field to an instance of the lambda
425 */
426 private void generateClassInitializer() {
427 String lambdaTypeDescriptor = factoryType.returnType().descriptorString();
428
429 // Generate the static final field that holds the lambda singleton
430 FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
431 LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null);
432 fv.visitEnd();
433
434 // Instantiate the lambda and store it to the static final field
448
449 /**
450 * Generate the constructor for the class
451 */
452 private void generateConstructor() {
453 // Generate constructor
454 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
455 constructorType.toMethodDescriptorString(), null, null);
456 ctor.visitCode();
457 ctor.visitVarInsn(ALOAD, 0);
458 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
459 METHOD_DESCRIPTOR_VOID, false);
460 int parameterCount = factoryType.parameterCount();
461 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
462 ctor.visitVarInsn(ALOAD, 0);
463 Class<?> argType = factoryType.parameterType(i);
464 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
465 lvIndex += getParameterSize(argType);
466 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
467 }
468
469 if (quotableOpField != null) {
470 generateQuotedFieldInitializer(ctor);
471 }
472
473 ctor.visitInsn(RETURN);
474 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
475 ctor.visitMaxs(-1, -1);
476 ctor.visitEnd();
477 }
478
479 private void generateQuotedFieldInitializer(MethodVisitor ctor) {
480 ctor.visitCode();
481
482 // push the receiver on the stack for operand of put field instruction
483 ctor.visitVarInsn(ALOAD, 0);
484
485 ctor.visitLdcInsn(makeQuotedMethodCondy);
486
487 // load op string from field
488
489 ctor.visitLdcInsn(reflectiveFieldCondy);
490 MethodType mtype = quotableOpFieldInfo.getMethodType();
491 if (quotableOpFieldInfo.getReferenceKind() != MethodHandleInfo.REF_getStatic) {
492 mtype = mtype.insertParameterTypes(0, implClass);
493 }
494 ctor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
495 "invokeExact", mtype.descriptorString(), false);
496
497 // load captured args in array
498
499 ctor.visitLdcInsn(quotableOpType.parameterCount());
500 ctor.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
501 int capturedArity = factoryType.parameterCount() - reflectiveCaptureCount();
502 // initialize quoted captures
503 TypeConvertingMethodAdapter tcmv = new TypeConvertingMethodAdapter(ctor);
504 for (int i = 0; i < reflectiveCaptureCount(); i++) {
505 ctor.visitInsn(DUP);
506 ctor.visitIntInsn(BIPUSH, i); // is it possible that i can be greater than Byte.MAX_VALUE ?
507 ctor.visitVarInsn(ALOAD, 0);
508 ctor.visitFieldInsn(GETFIELD, lambdaClassName, argNames[capturedArity + i], argDescs[capturedArity + i]);
509 tcmv.boxIfTypePrimitive(Type.getType(argDescs[capturedArity + i]));
510 ctor.visitInsn(AASTORE);
511 }
512
513 // now create a Quoted from String and captured args Object[]
514
515 ctor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
516 "invokeExact", HANDLE_MAKE_QUOTED.type().toMethodDescriptorString(), false);
517 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, quotedInstanceFieldName, quotedInstanceFieldDesc);
518 }
519
520 /**
521 * Generate a writeReplace method that supports serialization
522 */
523 private void generateSerializationFriendlyMethods() {
524 TypeConvertingMethodAdapter mv
525 = new TypeConvertingMethodAdapter(
526 cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
527 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
528 null, null));
529
530 mv.visitCode();
531 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
532 mv.visitInsn(DUP);
533 mv.visitLdcInsn(Type.getType(targetClass));
534 mv.visitLdcInsn(factoryType.returnType().getName().replace('.', '/'));
535 mv.visitLdcInsn(interfaceMethodName);
536 mv.visitLdcInsn(interfaceMethodType.toMethodDescriptorString());
537 mv.visitLdcInsn(implInfo.getReferenceKind());
538 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
539 mv.visitLdcInsn(implInfo.getName());
540 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
541 mv.visitLdcInsn(dynamicMethodType.toMethodDescriptorString());
542 mv.iconst(argDescs.length);
543 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
544 for (int i = 0; i < argDescs.length; i++) {
545 mv.visitInsn(DUP);
546 mv.iconst(i);
547 mv.visitVarInsn(ALOAD, 0);
548 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
549 mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
550 mv.visitInsn(AASTORE);
551 }
552 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
553 DESCR_CTOR_SERIALIZED_LAMBDA, false);
554 mv.visitInsn(ARETURN);
555 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
556 mv.visitMaxs(-1, -1);
557 mv.visitEnd();
558 }
559
560 /**
561 * Generate a writeReplace method that supports serialization
562 */
563 private void generateQuotableMethod() {
564 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL,
565 NAME_METHOD_QUOTED, DESCR_METHOD_QUOTED,
566 null, null);
567
568 mv.visitVarInsn(ALOAD, 0);
569 mv.visitFieldInsn(GETFIELD, lambdaClassName, quotedInstanceFieldName, quotedInstanceFieldDesc);
570 mv.visitInsn(ARETURN);
571
572 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
573 mv.visitMaxs(-1, -1);
574 mv.visitEnd();
575 }
576
577 /**
578 * Generate a readObject/writeObject method that is hostile to serialization
579 */
580 private void generateSerializationHostileMethods() {
581 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
582 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
583 null, SER_HOSTILE_EXCEPTIONS);
584 mv.visitCode();
585 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
586 mv.visitInsn(DUP);
587 mv.visitLdcInsn("Non-serializable lambda");
588 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
589 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
590 mv.visitInsn(ATHROW);
591 mv.visitMaxs(-1, -1);
592 mv.visitEnd();
593
594 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
595 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
596 null, SER_HOSTILE_EXCEPTIONS);
608 /**
609 * This class generates a method body which calls the lambda implementation
610 * method, converting arguments, as needed.
611 */
612 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
613
614 ForwardingMethodGenerator(MethodVisitor mv) {
615 super(mv);
616 }
617
618 void generate(MethodType methodType) {
619 visitCode();
620
621 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
622 visitTypeInsn(NEW, implMethodClassName);
623 visitInsn(DUP);
624 }
625 if (useImplMethodHandle) {
626 visitLdcInsn(implMethodCondy);
627 }
628 for (int i = 0; i < argNames.length - reflectiveCaptureCount(); i++) {
629 visitVarInsn(ALOAD, 0);
630 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
631 }
632
633 convertArgumentTypes(methodType);
634
635 if (useImplMethodHandle) {
636 MethodType mtype = implInfo.getMethodType();
637 if (implKind != MethodHandleInfo.REF_invokeStatic) {
638 mtype = mtype.insertParameterTypes(0, implClass);
639 }
640 visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
641 "invokeExact", mtype.descriptorString(), false);
642 } else {
643 // Invoke the method we want to forward to
644 visitMethodInsn(invocationOpcode(), implMethodClassName,
645 implMethodName, implMethodDesc,
646 implClass.isInterface());
647 }
648 // Convert the return value (if any) and return it
649 // Note: if adapting from non-void to void, the 'return'
650 // instruction will pop the unneeded result
651 Class<?> implReturnClass = implMethodType.returnType();
652 Class<?> samReturnClass = methodType.returnType();
653 convertType(implReturnClass, samReturnClass, samReturnClass);
654 visitInsn(getReturnOpcode(samReturnClass));
655 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
656 visitMaxs(-1, -1);
657 visitEnd();
658 }
659
660 private void convertArgumentTypes(MethodType samType) {
661 int lvIndex = 0;
662 int samParametersLength = samType.parameterCount();
663 int captureArity = factoryType.parameterCount() - reflectiveCaptureCount();
664 for (int i = 0; i < samParametersLength; i++) {
665 Class<?> argType = samType.parameterType(i);
666 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
667 lvIndex += getParameterSize(argType);
668 convertType(argType, implMethodType.parameterType(captureArity + i), dynamicMethodType.parameterType(i));
669 }
670 }
671
672 private int invocationOpcode() throws InternalError {
673 return switch (implKind) {
674 case MethodHandleInfo.REF_invokeStatic -> INVOKESTATIC;
675 case MethodHandleInfo.REF_newInvokeSpecial -> INVOKESPECIAL;
676 case MethodHandleInfo.REF_invokeVirtual -> INVOKEVIRTUAL;
677 case MethodHandleInfo.REF_invokeInterface -> INVOKEINTERFACE;
678 case MethodHandleInfo.REF_invokeSpecial -> INVOKESPECIAL;
679 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
680 };
681 }
682 }
683
702 return RETURN;
703 }
704 return IRETURN + getOpcodeOffset(c);
705 }
706
707 private static int getOpcodeOffset(Class<?> c) {
708 if (c.isPrimitive()) {
709 if (c == Long.TYPE) {
710 return 1;
711 } else if (c == Float.TYPE) {
712 return 2;
713 } else if (c == Double.TYPE) {
714 return 3;
715 }
716 return 0;
717 } else {
718 return 4;
719 }
720 }
721
722 private static Quoted makeQuoted(String opText, Object[] args) {
723 FuncOp op = (FuncOp)OpParser.fromStringOfFuncOp(opText);
724 return (Quoted)Interpreter.invoke(Lookup.IMPL_LOOKUP, op, args);
725 }
726 }
|