Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
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.
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
26 package java.lang.invoke;
28 import jdk.internal.org.objectweb.asm.*;
29 import sun.invoke.util.BytecodeDescriptor;
30 import sun.misc.Unsafe;
31 import sun.security.action.GetPropertyAction;
33 import java.io.FilePermission;
34 import java.io.Serializable;
35 import java.lang.reflect.Constructor;
36 import java.security.AccessController;
37 import java.security.PrivilegedAction;
38 import java.util.LinkedHashSet;
39 import java.util.concurrent.atomic.AtomicInteger;
40 import java.util.PropertyPermission;
43 import static jdk.internal.org.objectweb.asm.Opcodes.*;
46 * Lambda metafactory implementation which dynamically creates an
47 * inner-class-like class per lambda callsite.
49 * @see LambdaMetafactory
51 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
52 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
54 private static final int CLASSFILE_VERSION = 52;
55 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
56 private static final String JAVA_LANG_OBJECT = "java/lang/Object";
57 private static final String NAME_CTOR = "<init>";
58 private static final String NAME_FACTORY = "get$Lambda";
60 //Serialization support
61 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
62 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
63 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
64 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
65 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
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 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
70 = MethodType.methodType(void.class,
72 String.class, String.class, String.class,
73 int.class, String.class, String.class, String.class,
75 Object[].class).toMethodDescriptorString();
76 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION
77 = MethodType.methodType(void.class, String.class).toMethodDescriptorString();
78 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
81 private static final String[] EMPTY_STRING_ARRAY = new String[0];
83 // Used to ensure that each spun class name is unique
84 private static final AtomicInteger counter = new AtomicInteger(0);
86 // For dumping generated classes to disk, for debugging purposes
87 private static final ProxyClassesDumper dumper;
90 final String key = "jdk.internal.lambda.dumpProxyClasses";
91 String path = AccessController.doPrivileged(
92 new GetPropertyAction(key), null,
93 new PropertyPermission(key , "read"));
94 dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
97 // See context values in AbstractValidatingLambdaMetafactory
98 private final String implMethodClassName; // Name of type containing implementation "CC"
99 private final String implMethodName; // Name of implementation method "impl"
100 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
101 private final Class<?> implMethodReturnClass; // class for implementaion method return type "Ljava/lang/String;"
102 private final MethodType constructorType; // Generated class constructor type "(CC)void"
103 private final ClassWriter cw; // ASM class writer
104 private final String[] argNames; // Generated names for the constructor arguments
105 private final String[] argDescs; // Type descriptors for the constructor arguments
106 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
109 * General meta-factory constructor, supporting both standard cases and
110 * allowing for uncommon options such as serialization or bridging.
112 * @param caller Stacked automatically by VM; represents a lookup context
113 * with the accessibility privileges of the caller.
114 * @param invokedType Stacked automatically by VM; the signature of the
115 * invoked method, which includes the expected static
116 * type of the returned lambda object, and the static
117 * types of the captured arguments for the lambda. In
118 * the event that the implementation method is an
119 * instance method, the first argument in the invocation
120 * signature will correspond to the receiver.
121 * @param samMethodName Name of the method in the functional interface to
122 * which the lambda or method reference is being
123 * converted, represented as a String.
124 * @param samMethodType Type of the method in the functional interface to
125 * which the lambda or method reference is being
126 * converted, represented as a MethodType.
127 * @param implMethod The implementation method which should be called (with
128 * suitable adaptation of argument types, return types,
129 * and adjustment for captured arguments) when methods of
130 * the resulting functional interface instance are invoked.
131 * @param instantiatedMethodType The signature of the primary functional
132 * interface method after type variables are
133 * substituted with their instantiation from
135 * @param isSerializable Should the lambda be made serializable? If set,
136 * either the target type or one of the additional SAM
137 * types must extend {@code Serializable}.
138 * @param markerInterfaces Additional interfaces which the lambda object
140 * @param additionalBridges Method types for additional signatures to be
141 * bridged to the implementation method
142 * @throws LambdaConversionException If any of the meta-factory protocol
143 * invariants are violated
145 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
146 MethodType invokedType,
147 String samMethodName,
148 MethodType samMethodType,
149 MethodHandle implMethod,
150 MethodType instantiatedMethodType,
151 boolean isSerializable,
152 Class<?>[] markerInterfaces,
153 MethodType[] additionalBridges)
154 throws LambdaConversionException {
155 super(caller, invokedType, samMethodName, samMethodType,
156 implMethod, instantiatedMethodType,
157 isSerializable, markerInterfaces, additionalBridges);
158 implMethodClassName = implDefiningClass.getName().replace('.', '/');
159 implMethodName = implInfo.getName();
160 implMethodDesc = implMethodType.toMethodDescriptorString();
161 implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
163 : implMethodType.returnType();
164 constructorType = invokedType.changeReturnType(Void.TYPE);
165 lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
166 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
167 int parameterCount = invokedType.parameterCount();
168 if (parameterCount > 0) {
169 argNames = new String[parameterCount];
170 argDescs = new String[parameterCount];
171 for (int i = 0; i < parameterCount; i++) {
172 argNames[i] = "arg$" + (i + 1);
173 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
176 argNames = argDescs = EMPTY_STRING_ARRAY;
181 * Build the CallSite. Generate a class file which implements the functional
182 * interface, define the class, if there are no parameters create an instance
183 * of the class which the CallSite will return, otherwise, generate handles
184 * which will call the class' constructor.
186 * @return a CallSite, which, when invoked, will return an instance of the
187 * functional interface
188 * @throws ReflectiveOperationException
189 * @throws LambdaConversionException If properly formed functional interface
193 CallSite buildCallSite() throws LambdaConversionException {
194 final Class<?> innerClass = spinInnerClass();
195 if (invokedType.parameterCount() == 0) {
196 final Constructor[] ctrs = AccessController.doPrivileged(
197 new PrivilegedAction<Constructor[]>() {
199 public Constructor[] run() {
200 Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
201 if (ctrs.length == 1) {
202 // The lambda implementing inner class constructor is private, set
203 // it accessible (by us) before creating the constant sole instance
204 ctrs[0].setAccessible(true);
209 if (ctrs.length != 1) {
210 throw new LambdaConversionException("Expected one lambda constructor for "
211 + innerClass.getCanonicalName() + ", got " + ctrs.length);
215 Object inst = ctrs[0].newInstance();
216 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
218 catch (ReflectiveOperationException e) {
219 throw new LambdaConversionException("Exception instantiating lambda object", e);
223 UNSAFE.ensureClassInitialized(innerClass);
224 return new ConstantCallSite(
225 MethodHandles.Lookup.IMPL_LOOKUP
226 .findStatic(innerClass, NAME_FACTORY, invokedType));
228 catch (ReflectiveOperationException e) {
229 throw new LambdaConversionException("Exception finding constructor", e);
235 * Generate a class file which implements the functional
236 * interface, define and return the class.
238 * @implNote The class that is generated does not include signature
239 * information for exceptions that may be present on the SAM method.
240 * This is to reduce classfile size, and is harmless as checked exceptions
241 * are erased anyway, no one will ever compile against this classfile,
242 * and we make no guarantees about the reflective properties of lambda
245 * @return a Class which implements the functional interface
246 * @throws LambdaConversionException If properly formed functional interface
249 private Class<?> spinInnerClass() throws LambdaConversionException {
251 String samIntf = samBase.getName().replace('.', '/');
252 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
253 if (markerInterfaces.length == 0) {
254 interfaces = new String[]{samIntf};
256 // Assure no duplicate interfaces (ClassFormatError)
257 Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
259 for (Class<?> markerInterface : markerInterfaces) {
260 itfs.add(markerInterface.getName().replace('.', '/'));
261 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
263 interfaces = itfs.toArray(new String[itfs.size()]);
266 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
267 lambdaClassName, null,
268 JAVA_LANG_OBJECT, interfaces);
270 // Generate final fields to be filled in by constructor
271 for (int i = 0; i < argDescs.length; i++) {
272 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
279 generateConstructor();
281 if (invokedType.parameterCount() != 0) {
285 // Forward the SAM method
286 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
287 samMethodType.toMethodDescriptorString(), null, null);
288 new ForwardingMethodGenerator(mv).generate(samMethodType);
290 // Forward the bridges
291 if (additionalBridges != null) {
292 for (MethodType mt : additionalBridges) {
293 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
294 mt.toMethodDescriptorString(), null, null);
295 new ForwardingMethodGenerator(mv).generate(mt);
300 generateSerializationFriendlyMethods();
301 else if (accidentallySerializable)
302 generateSerializationHostileMethods();
306 // Define the generated class in this VM.
308 final byte[] classBytes = cw.toByteArray();
310 // If requested, dump out to a file for debugging purposes
311 if (dumper != null) {
312 AccessController.doPrivileged(new PrivilegedAction<Void>() {
315 dumper.dumpClass(lambdaClassName, classBytes);
319 new FilePermission("<<ALL FILES>>", "read, write"),
320 // createDirectories may need it
321 new PropertyPermission("user.dir", "read"));
324 return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
328 * Generate the factory method for the class
330 private void generateFactory() {
331 MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
333 m.visitTypeInsn(NEW, lambdaClassName);
334 m.visitInsn(Opcodes.DUP);
335 int parameterCount = invokedType.parameterCount();
336 for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
337 Class<?> argType = invokedType.parameterType(typeIndex);
338 m.visitVarInsn(getLoadOpcode(argType), varIndex);
339 varIndex += getParameterSize(argType);
341 m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString());
342 m.visitInsn(ARETURN);
348 * Generate the constructor for the class
350 private void generateConstructor() {
351 // Generate constructor
352 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
353 constructorType.toMethodDescriptorString(), null, null);
355 ctor.visitVarInsn(ALOAD, 0);
356 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
357 METHOD_DESCRIPTOR_VOID);
358 int parameterCount = invokedType.parameterCount();
359 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
360 ctor.visitVarInsn(ALOAD, 0);
361 Class<?> argType = invokedType.parameterType(i);
362 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
363 lvIndex += getParameterSize(argType);
364 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
366 ctor.visitInsn(RETURN);
367 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
368 ctor.visitMaxs(-1, -1);
373 * Generate a writeReplace method that supports serialization
375 private void generateSerializationFriendlyMethods() {
376 TypeConvertingMethodAdapter mv
377 = new TypeConvertingMethodAdapter(
378 cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
379 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
383 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
385 mv.visitLdcInsn(Type.getType(targetClass));
386 mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
387 mv.visitLdcInsn(samMethodName);
388 mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
389 mv.visitLdcInsn(implInfo.getReferenceKind());
390 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
391 mv.visitLdcInsn(implInfo.getName());
392 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
393 mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
394 mv.iconst(argDescs.length);
395 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
396 for (int i = 0; i < argDescs.length; i++) {
399 mv.visitVarInsn(ALOAD, 0);
400 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
401 mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
402 mv.visitInsn(AASTORE);
404 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
405 DESCR_CTOR_SERIALIZED_LAMBDA);
406 mv.visitInsn(ARETURN);
407 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
408 mv.visitMaxs(-1, -1);
413 * Generate a readObject/writeObject method that is hostile to serialization
415 private void generateSerializationHostileMethods() {
416 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
417 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
418 null, SER_HOSTILE_EXCEPTIONS);
420 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
422 mv.visitLdcInsn("Non-serializable lambda");
423 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
424 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION);
425 mv.visitInsn(ATHROW);
426 mv.visitMaxs(-1, -1);
429 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
430 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
431 null, SER_HOSTILE_EXCEPTIONS);
433 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
435 mv.visitLdcInsn("Non-serializable lambda");
436 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
437 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION);
438 mv.visitInsn(ATHROW);
439 mv.visitMaxs(-1, -1);
444 * This class generates a method body which calls the lambda implementation
445 * method, converting arguments, as needed.
447 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
449 ForwardingMethodGenerator(MethodVisitor mv) {
453 void generate(MethodType methodType) {
456 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
457 visitTypeInsn(NEW, implMethodClassName);
460 for (int i = 0; i < argNames.length; i++) {
461 visitVarInsn(ALOAD, 0);
462 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
465 convertArgumentTypes(methodType);
467 // Invoke the method we want to forward to
468 visitMethodInsn(invocationOpcode(), implMethodClassName,
469 implMethodName, implMethodDesc,
470 implDefiningClass.isInterface());
472 // Convert the return value (if any) and return it
473 // Note: if adapting from non-void to void, the 'return'
474 // instruction will pop the unneeded result
475 Class<?> samReturnClass = methodType.returnType();
476 convertType(implMethodReturnClass, samReturnClass, samReturnClass);
477 visitInsn(getReturnOpcode(samReturnClass));
478 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
483 private void convertArgumentTypes(MethodType samType) {
485 boolean samIncludesReceiver = implIsInstanceMethod &&
486 invokedType.parameterCount() == 0;
487 int samReceiverLength = samIncludesReceiver ? 1 : 0;
488 if (samIncludesReceiver) {
490 Class<?> rcvrType = samType.parameterType(0);
491 visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1);
492 lvIndex += getParameterSize(rcvrType);
493 convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0));
495 int samParametersLength = samType.parameterCount();
496 int argOffset = implMethodType.parameterCount() - samParametersLength;
497 for (int i = samReceiverLength; i < samParametersLength; i++) {
498 Class<?> argType = samType.parameterType(i);
499 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
500 lvIndex += getParameterSize(argType);
501 convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i));
505 private int invocationOpcode() throws InternalError {
507 case MethodHandleInfo.REF_invokeStatic:
509 case MethodHandleInfo.REF_newInvokeSpecial:
510 return INVOKESPECIAL;
511 case MethodHandleInfo.REF_invokeVirtual:
512 return INVOKEVIRTUAL;
513 case MethodHandleInfo.REF_invokeInterface:
514 return INVOKEINTERFACE;
515 case MethodHandleInfo.REF_invokeSpecial:
516 return INVOKESPECIAL;
518 throw new InternalError("Unexpected invocation kind: " + implKind);
523 static int getParameterSize(Class<?> c) {
524 if (c == Void.TYPE) {
526 } else if (c == Long.TYPE || c == Double.TYPE) {
532 static int getLoadOpcode(Class<?> c) {
534 throw new InternalError("Unexpected void type of load opcode");
536 return ILOAD + getOpcodeOffset(c);
539 static int getReturnOpcode(Class<?> c) {
543 return IRETURN + getOpcodeOffset(c);
546 private static int getOpcodeOffset(Class<?> c) {
547 if (c.isPrimitive()) {
548 if (c == Long.TYPE) {
550 } else if (c == Float.TYPE) {
552 } else if (c == Double.TYPE) {