10 *
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).
16 *
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.org.objectweb.asm.*;
29 import sun.invoke.util.BytecodeDescriptor;
30 import jdk.internal.misc.Unsafe;
31 import sun.security.action.GetPropertyAction;
32 import sun.security.action.GetBooleanAction;
33
34 import java.io.FilePermission;
35 import java.io.Serializable;
36 import java.lang.reflect.Constructor;
37 import java.security.AccessController;
38 import java.security.PrivilegedAction;
39 import java.util.LinkedHashSet;
40 import java.util.concurrent.atomic.AtomicInteger;
41 import java.util.PropertyPermission;
42 import java.util.Set;
43
44 import static jdk.internal.org.objectweb.asm.Opcodes.*;
45
46 /**
47 * Lambda metafactory implementation which dynamically creates an
48 * inner-class-like class per lambda callsite.
49 *
50 * @see LambdaMetafactory
51 */
52 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
53 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
54
55 private static final int CLASSFILE_VERSION = 52;
56 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
57 private static final String JAVA_LANG_OBJECT = "java/lang/Object";
58 private static final String NAME_CTOR = "<init>";
59 private static final String NAME_FACTORY = "get$Lambda";
60
61 //Serialization support
62 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
63 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
64 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
65 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
66 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
67 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
68 private static final String NAME_METHOD_READ_OBJECT = "readObject";
69 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
70
71 private static final String DESCR_CLASS = "Ljava/lang/Class;";
72 private static final String DESCR_STRING = "Ljava/lang/String;";
73 private static final String DESCR_OBJECT = "Ljava/lang/Object;";
74 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
75 = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
76 + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
77
78 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
79 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
80
81 private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
82
83 private static final String[] EMPTY_STRING_ARRAY = new String[0];
84
85 // Used to ensure that each spun class name is unique
86 private static final AtomicInteger counter = new AtomicInteger(0);
87
88 // For dumping generated classes to disk, for debugging purposes
89 private static final ProxyClassesDumper dumper;
90
91 private static final boolean disableEagerInitialization;
92
93 static {
94 final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
95 String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
96 dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
97
98 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
99 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
100 }
101
102 // See context values in AbstractValidatingLambdaMetafactory
103 private final String implMethodClassName; // Name of type containing implementation "CC"
104 private final String implMethodName; // Name of implementation method "impl"
105 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
106 private final MethodType constructorType; // Generated class constructor type "(CC)void"
107 private final ClassWriter cw; // ASM class writer
108 private final String[] argNames; // Generated names for the constructor arguments
109 private final String[] argDescs; // Type descriptors for the constructor arguments
110 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
111
112 /**
113 * General meta-factory constructor, supporting both standard cases and
114 * allowing for uncommon options such as serialization or bridging.
115 *
116 * @param caller Stacked automatically by VM; represents a lookup context
117 * with the accessibility privileges of the caller.
118 * @param invokedType Stacked automatically by VM; the signature of the
119 * invoked method, which includes the expected static
120 * type of the returned lambda object, and the static
121 * types of the captured arguments for the lambda. In
122 * the event that the implementation method is an
123 * instance method, the first argument in the invocation
124 * signature will correspond to the receiver.
125 * @param samMethodName Name of the method in the functional interface to
126 * which the lambda or method reference is being
127 * converted, represented as a String.
128 * @param samMethodType Type of the method in the functional interface to
129 * which the lambda or method reference is being
130 * converted, represented as a MethodType.
146 * @throws LambdaConversionException If any of the meta-factory protocol
147 * invariants are violated
148 */
149 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
150 MethodType invokedType,
151 String samMethodName,
152 MethodType samMethodType,
153 MethodHandle implMethod,
154 MethodType instantiatedMethodType,
155 boolean isSerializable,
156 Class<?>[] markerInterfaces,
157 MethodType[] additionalBridges)
158 throws LambdaConversionException {
159 super(caller, invokedType, samMethodName, samMethodType,
160 implMethod, instantiatedMethodType,
161 isSerializable, markerInterfaces, additionalBridges);
162 implMethodClassName = implClass.getName().replace('.', '/');
163 implMethodName = implInfo.getName();
164 implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
165 constructorType = invokedType.changeReturnType(Void.TYPE);
166 lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
167 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
168 int parameterCount = invokedType.parameterCount();
169 if (parameterCount > 0) {
170 argNames = new String[parameterCount];
171 argDescs = new String[parameterCount];
172 for (int i = 0; i < parameterCount; i++) {
173 argNames[i] = "arg$" + (i + 1);
174 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
175 }
176 } else {
177 argNames = argDescs = EMPTY_STRING_ARRAY;
178 }
179 }
180
181 /**
182 * Build the CallSite. Generate a class file which implements the functional
183 * interface, define the class, if there are no parameters create an instance
184 * of the class which the CallSite will return, otherwise, generate handles
185 * which will call the class' constructor.
186 *
187 * @return a CallSite, which, when invoked, will return an instance of the
188 * functional interface
189 * @throws ReflectiveOperationException
190 * @throws LambdaConversionException If properly formed functional interface
191 * is not found
192 */
193 @Override
194 CallSite buildCallSite() throws LambdaConversionException {
195 final Class<?> innerClass = spinInnerClass();
196 if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
197 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
198 // unless we've suppressed eager initialization
199 final Constructor<?>[] ctrs = AccessController.doPrivileged(
200 new PrivilegedAction<>() {
201 @Override
202 public Constructor<?>[] run() {
203 Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
204 if (ctrs.length == 1) {
205 // The lambda implementing inner class constructor is private, set
206 // it accessible (by us) before creating the constant sole instance
207 ctrs[0].setAccessible(true);
208 }
209 return ctrs;
210 }
211 });
212 if (ctrs.length != 1) {
213 throw new LambdaConversionException("Expected one lambda constructor for "
214 + innerClass.getCanonicalName() + ", got " + ctrs.length);
215 }
216
217 try {
218 Object inst = ctrs[0].newInstance();
219 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
220 }
221 catch (ReflectiveOperationException e) {
222 throw new LambdaConversionException("Exception instantiating lambda object", e);
223 }
224 } else {
225 try {
226 if (!disableEagerInitialization) {
227 UNSAFE.ensureClassInitialized(innerClass);
228 }
229 return new ConstantCallSite(
230 MethodHandles.Lookup.IMPL_LOOKUP
231 .findStatic(innerClass, NAME_FACTORY, invokedType));
232 }
233 catch (ReflectiveOperationException e) {
234 throw new LambdaConversionException("Exception finding constructor", e);
235 }
236 }
237 }
238
239 /**
240 * Generate a class file which implements the functional
241 * interface, define and return the class.
242 *
243 * @implNote The class that is generated does not include signature
244 * information for exceptions that may be present on the SAM method.
245 * This is to reduce classfile size, and is harmless as checked exceptions
246 * are erased anyway, no one will ever compile against this classfile,
247 * and we make no guarantees about the reflective properties of lambda
248 * objects.
249 *
250 * @return a Class which implements the functional interface
251 * @throws LambdaConversionException If properly formed functional interface
252 * is not found
253 */
266 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
267 }
268 interfaces = itfs.toArray(new String[itfs.size()]);
269 }
270
271 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
272 lambdaClassName, null,
273 JAVA_LANG_OBJECT, interfaces);
274
275 // Generate final fields to be filled in by constructor
276 for (int i = 0; i < argDescs.length; i++) {
277 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
278 argNames[i],
279 argDescs[i],
280 null, null);
281 fv.visitEnd();
282 }
283
284 generateConstructor();
285
286 if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
287 generateFactory();
288 }
289
290 // Forward the SAM method
291 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
292 samMethodType.toMethodDescriptorString(), null, null);
293 mv.visitAnnotation(DESCR_HIDDEN, true);
294 new ForwardingMethodGenerator(mv).generate(samMethodType);
295
296 // Forward the bridges
297 if (additionalBridges != null) {
298 for (MethodType mt : additionalBridges) {
299 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
300 mt.toMethodDescriptorString(), null, null);
301 mv.visitAnnotation(DESCR_HIDDEN, true);
302 new ForwardingMethodGenerator(mv).generate(mt);
303 }
304 }
305
306 if (isSerializable)
307 generateSerializationFriendlyMethods();
308 else if (accidentallySerializable)
309 generateSerializationHostileMethods();
310
311 cw.visitEnd();
312
313 // Define the generated class in this VM.
314
315 final byte[] classBytes = cw.toByteArray();
316
317 // If requested, dump out to a file for debugging purposes
318 if (dumper != null) {
319 AccessController.doPrivileged(new PrivilegedAction<>() {
320 @Override
321 public Void run() {
322 dumper.dumpClass(lambdaClassName, classBytes);
323 return null;
324 }
325 }, null,
326 new FilePermission("<<ALL FILES>>", "read, write"),
327 // createDirectories may need it
328 new PropertyPermission("user.dir", "read"));
329 }
330
331 return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
332 }
333
334 /**
335 * Generate the factory method for the class
336 */
337 private void generateFactory() {
338 MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
339 m.visitCode();
340 m.visitTypeInsn(NEW, lambdaClassName);
341 m.visitInsn(Opcodes.DUP);
342 int parameterCount = invokedType.parameterCount();
343 for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
344 Class<?> argType = invokedType.parameterType(typeIndex);
345 m.visitVarInsn(getLoadOpcode(argType), varIndex);
346 varIndex += getParameterSize(argType);
347 }
348 m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
349 m.visitInsn(ARETURN);
350 m.visitMaxs(-1, -1);
351 m.visitEnd();
352 }
353
354 /**
355 * Generate the constructor for the class
356 */
357 private void generateConstructor() {
358 // Generate constructor
359 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
360 constructorType.toMethodDescriptorString(), null, null);
361 ctor.visitCode();
362 ctor.visitVarInsn(ALOAD, 0);
363 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
364 METHOD_DESCRIPTOR_VOID, false);
365 int parameterCount = invokedType.parameterCount();
366 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
367 ctor.visitVarInsn(ALOAD, 0);
368 Class<?> argType = invokedType.parameterType(i);
369 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
370 lvIndex += getParameterSize(argType);
371 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
447 mv.visitEnd();
448 }
449
450 /**
451 * This class generates a method body which calls the lambda implementation
452 * method, converting arguments, as needed.
453 */
454 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
455
456 ForwardingMethodGenerator(MethodVisitor mv) {
457 super(mv);
458 }
459
460 void generate(MethodType methodType) {
461 visitCode();
462
463 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
464 visitTypeInsn(NEW, implMethodClassName);
465 visitInsn(DUP);
466 }
467 for (int i = 0; i < argNames.length; i++) {
468 visitVarInsn(ALOAD, 0);
469 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
470 }
471
472 convertArgumentTypes(methodType);
473
474 // Invoke the method we want to forward to
475 visitMethodInsn(invocationOpcode(), implMethodClassName,
476 implMethodName, implMethodDesc,
477 implClass.isInterface());
478
479 // Convert the return value (if any) and return it
480 // Note: if adapting from non-void to void, the 'return'
481 // instruction will pop the unneeded result
482 Class<?> implReturnClass = implMethodType.returnType();
483 Class<?> samReturnClass = methodType.returnType();
484 convertType(implReturnClass, samReturnClass, samReturnClass);
485 visitInsn(getReturnOpcode(samReturnClass));
486 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
487 visitMaxs(-1, -1);
488 visitEnd();
489 }
490
491 private void convertArgumentTypes(MethodType samType) {
492 int lvIndex = 0;
493 int samParametersLength = samType.parameterCount();
494 int captureArity = invokedType.parameterCount();
495 for (int i = 0; i < samParametersLength; i++) {
496 Class<?> argType = samType.parameterType(i);
497 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
498 lvIndex += getParameterSize(argType);
|
10 *
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).
16 *
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.org.objectweb.asm.*;
29 import sun.invoke.util.BytecodeDescriptor;
30 import sun.security.action.GetPropertyAction;
31 import sun.security.action.GetBooleanAction;
32
33 import java.io.FilePermission;
34 import java.io.Serializable;
35 import java.lang.invoke.MethodHandles.Lookup;
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.Modifier;
38 import java.security.AccessController;
39 import java.security.PrivilegedAction;
40 import java.util.LinkedHashSet;
41 import java.util.concurrent.atomic.AtomicInteger;
42 import java.util.PropertyPermission;
43 import java.util.Set;
44
45 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
46 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
47 import static jdk.internal.org.objectweb.asm.Opcodes.*;
48
49 /**
50 * Lambda metafactory implementation which dynamically creates an
51 * inner-class-like class per lambda callsite.
52 *
53 * @see LambdaMetafactory
54 */
55 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
56 private static final int CLASSFILE_VERSION = 52;
57 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
58 private static final String JAVA_LANG_OBJECT = "java/lang/Object";
59 private static final String NAME_CTOR = "<init>";
60
61 //Serialization support
62 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
63 private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
64 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
65 private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
66 private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
67 private static final String DESCR_SET_IMPL_METHOD = "(Ljava/lang/invoke/MethodHandle;)V";
68
69 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
70 private static final String NAME_METHOD_READ_OBJECT = "readObject";
71 private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
72 private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";
73
74 private static final String DESCR_CLASS = "Ljava/lang/Class;";
75 private static final String DESCR_STRING = "Ljava/lang/String;";
76 private static final String DESCR_OBJECT = "Ljava/lang/Object;";
77 private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
78 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
79 = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
80 + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
81
82 private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
83 private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
84
85 private static final String[] EMPTY_STRING_ARRAY = new String[0];
86
87 // Used to ensure that each spun class name is unique
88 private static final AtomicInteger counter = new AtomicInteger(0);
89
90 // For dumping generated classes to disk, for debugging purposes
91 private static final ProxyClassesDumper dumper;
92
93 private static final boolean disableEagerInitialization;
94
95 static {
96 final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
97 String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
98 dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
99
100 final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
101 disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
102 }
103
104 // See context values in AbstractValidatingLambdaMetafactory
105 private final String implMethodClassName; // Name of type containing implementation "CC"
106 private final String implMethodName; // Name of implementation method "impl"
107 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
108 private final MethodType constructorType; // Generated class constructor type "(CC)void"
109 private final ClassWriter cw; // ASM class writer
110 private final String[] argNames; // Generated names for the constructor arguments
111 private final String[] argDescs; // Type descriptors for the constructor arguments
112 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
113 private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
114
115 /**
116 * General meta-factory constructor, supporting both standard cases and
117 * allowing for uncommon options such as serialization or bridging.
118 *
119 * @param caller Stacked automatically by VM; represents a lookup context
120 * with the accessibility privileges of the caller.
121 * @param invokedType Stacked automatically by VM; the signature of the
122 * invoked method, which includes the expected static
123 * type of the returned lambda object, and the static
124 * types of the captured arguments for the lambda. In
125 * the event that the implementation method is an
126 * instance method, the first argument in the invocation
127 * signature will correspond to the receiver.
128 * @param samMethodName Name of the method in the functional interface to
129 * which the lambda or method reference is being
130 * converted, represented as a String.
131 * @param samMethodType Type of the method in the functional interface to
132 * which the lambda or method reference is being
133 * converted, represented as a MethodType.
149 * @throws LambdaConversionException If any of the meta-factory protocol
150 * invariants are violated
151 */
152 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
153 MethodType invokedType,
154 String samMethodName,
155 MethodType samMethodType,
156 MethodHandle implMethod,
157 MethodType instantiatedMethodType,
158 boolean isSerializable,
159 Class<?>[] markerInterfaces,
160 MethodType[] additionalBridges)
161 throws LambdaConversionException {
162 super(caller, invokedType, samMethodName, samMethodType,
163 implMethod, instantiatedMethodType,
164 isSerializable, markerInterfaces, additionalBridges);
165 implMethodClassName = implClass.getName().replace('.', '/');
166 implMethodName = implInfo.getName();
167 implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
168 constructorType = invokedType.changeReturnType(Void.TYPE);
169 lambdaClassName = lambdaClassName(targetClass);
170 useImplMethodHandle = !implClass.getPackageName().equals(implInfo.getDeclaringClass().getPackageName())
171 && !Modifier.isPublic(implInfo.getModifiers());
172 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
173 int parameterCount = invokedType.parameterCount();
174 if (parameterCount > 0) {
175 argNames = new String[parameterCount];
176 argDescs = new String[parameterCount];
177 for (int i = 0; i < parameterCount; i++) {
178 argNames[i] = "arg$" + (i + 1);
179 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
180 }
181 } else {
182 argNames = argDescs = EMPTY_STRING_ARRAY;
183 }
184 }
185
186 private static String lambdaClassName(Class<?> targetClass) {
187 String name = targetClass.getName();
188 if (targetClass.isHiddenClass()) {
189 // use the original class name
190 name = name.replace('/', '_');
191 }
192 return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
193 }
194
195 /**
196 * Build the CallSite. Generate a class file which implements the functional
197 * interface, define the class, if there are no parameters create an instance
198 * of the class which the CallSite will return, otherwise, generate handles
199 * which will call the class' constructor.
200 *
201 * @return a CallSite, which, when invoked, will return an instance of the
202 * functional interface
203 * @throws ReflectiveOperationException
204 * @throws LambdaConversionException If properly formed functional interface
205 * is not found
206 */
207 @Override
208 CallSite buildCallSite() throws LambdaConversionException {
209 final Class<?> innerClass = spinInnerClass();
210 if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
211 // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
212 // unless we've suppressed eager initialization
213 final Constructor<?>[] ctrs = AccessController.doPrivileged(
214 new PrivilegedAction<>() {
215 @Override
216 public Constructor<?>[] run() {
217 Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
218 if (ctrs.length == 1) {
219 // The lambda implementing inner class constructor is private, set
220 // it accessible (by us) before creating the constant sole instance
221 ctrs[0].setAccessible(true);
222 }
223 return ctrs;
224 }
225 });
226 if (ctrs.length != 1) {
227 throw new LambdaConversionException("Expected one lambda constructor for "
228 + innerClass.getCanonicalName() + ", got " + ctrs.length);
229 }
230
231 try {
232 Object inst = ctrs[0].newInstance();
233 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
234 } catch (ReflectiveOperationException e) {
235 throw new LambdaConversionException("Exception instantiating lambda object", e);
236 }
237 } else {
238 try {
239 MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
240 return new ConstantCallSite(mh.asType(invokedType));
241 } catch (ReflectiveOperationException e) {
242 throw new LambdaConversionException("Exception finding constructor", e);
243 }
244 }
245 }
246
247 /**
248 * Generate a class file which implements the functional
249 * interface, define and return the class.
250 *
251 * @implNote The class that is generated does not include signature
252 * information for exceptions that may be present on the SAM method.
253 * This is to reduce classfile size, and is harmless as checked exceptions
254 * are erased anyway, no one will ever compile against this classfile,
255 * and we make no guarantees about the reflective properties of lambda
256 * objects.
257 *
258 * @return a Class which implements the functional interface
259 * @throws LambdaConversionException If properly formed functional interface
260 * is not found
261 */
274 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
275 }
276 interfaces = itfs.toArray(new String[itfs.size()]);
277 }
278
279 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
280 lambdaClassName, null,
281 JAVA_LANG_OBJECT, interfaces);
282
283 // Generate final fields to be filled in by constructor
284 for (int i = 0; i < argDescs.length; i++) {
285 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
286 argNames[i],
287 argDescs[i],
288 null, null);
289 fv.visitEnd();
290 }
291
292 generateConstructor();
293
294 // Forward the SAM method
295 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
296 samMethodType.toMethodDescriptorString(), null, null);
297 new ForwardingMethodGenerator(mv).generate(samMethodType);
298
299 // Forward the bridges
300 if (additionalBridges != null) {
301 for (MethodType mt : additionalBridges) {
302 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
303 mt.toMethodDescriptorString(), null, null);
304 new ForwardingMethodGenerator(mv).generate(mt);
305 }
306 }
307
308 if (useImplMethodHandle) {
309 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
310 NAME_FIELD_IMPL_METHOD,
311 DESCR_METHOD_HANDLE,
312 null, null);
313 fv.visitEnd();
314
315 mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC,
316 "setImplMethod", DESCR_SET_IMPL_METHOD,
317 null, null);
318 mv.visitVarInsn(ALOAD, 0);
319 mv.visitFieldInsn(PUTSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
320 mv.visitInsn(RETURN);
321 mv.visitMaxs(-1, -1);
322 mv.visitEnd();
323 }
324
325 if (isSerializable)
326 generateSerializationFriendlyMethods();
327 else if (accidentallySerializable)
328 generateSerializationHostileMethods();
329
330 cw.visitEnd();
331
332 // Define the generated class in this VM.
333
334 final byte[] classBytes = cw.toByteArray();
335 // If requested, dump out to a file for debugging purposes
336 if (dumper != null) {
337 AccessController.doPrivileged(new PrivilegedAction<>() {
338 @Override
339 public Void run() {
340 dumper.dumpClass(lambdaClassName, classBytes);
341 return null;
342 }
343 }, null,
344 new FilePermission("<<ALL FILES>>", "read, write"),
345 // createDirectories may need it
346 new PropertyPermission("user.dir", "read"));
347 }
348 try {
349 // this class is linked at the indy callsite; so define a hidden nestmate
350 Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
351 if (useImplMethodHandle) {
352 // If the target class invokes a method reference this::m which is
353 // resolved to a protected method inherited from a superclass in a different
354 // package, the target class does not have a bridge and this method reference
355 // has been changed from public to protected after the target class was compiled.
356 // This lambda proxy class has no access to the resolved method.
357 // So this workaround by passing the live implMethod method handle
358 // to the proxy class to invoke directly.
359 MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
360 mh.invokeExact(implMethod);
361 }
362 return lookup.lookupClass();
363 } catch (IllegalAccessException e) {
364 throw new LambdaConversionException("Exception defining lambda proxy class", e);
365 } catch (Throwable t) {
366 throw new InternalError(t);
367 }
368 }
369
370 /**
371 * Generate the constructor for the class
372 */
373 private void generateConstructor() {
374 // Generate constructor
375 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
376 constructorType.toMethodDescriptorString(), null, null);
377 ctor.visitCode();
378 ctor.visitVarInsn(ALOAD, 0);
379 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
380 METHOD_DESCRIPTOR_VOID, false);
381 int parameterCount = invokedType.parameterCount();
382 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
383 ctor.visitVarInsn(ALOAD, 0);
384 Class<?> argType = invokedType.parameterType(i);
385 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
386 lvIndex += getParameterSize(argType);
387 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
463 mv.visitEnd();
464 }
465
466 /**
467 * This class generates a method body which calls the lambda implementation
468 * method, converting arguments, as needed.
469 */
470 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
471
472 ForwardingMethodGenerator(MethodVisitor mv) {
473 super(mv);
474 }
475
476 void generate(MethodType methodType) {
477 visitCode();
478
479 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
480 visitTypeInsn(NEW, implMethodClassName);
481 visitInsn(DUP);
482 }
483 if (useImplMethodHandle) {
484 visitVarInsn(ALOAD, 0);
485 visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
486 }
487 for (int i = 0; i < argNames.length; i++) {
488 visitVarInsn(ALOAD, 0);
489 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
490 }
491
492 convertArgumentTypes(methodType);
493
494 if (useImplMethodHandle) {
495 MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
496 visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
497 "invokeExact", mtype.descriptorString(), false);
498 } else {
499 // Invoke the method we want to forward to
500 visitMethodInsn(invocationOpcode(), implMethodClassName,
501 implMethodName, implMethodDesc,
502 implClass.isInterface());
503 }
504 // Convert the return value (if any) and return it
505 // Note: if adapting from non-void to void, the 'return'
506 // instruction will pop the unneeded result
507 Class<?> implReturnClass = implMethodType.returnType();
508 Class<?> samReturnClass = methodType.returnType();
509 convertType(implReturnClass, samReturnClass, samReturnClass);
510 visitInsn(getReturnOpcode(samReturnClass));
511 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
512 visitMaxs(-1, -1);
513 visitEnd();
514 }
515
516 private void convertArgumentTypes(MethodType samType) {
517 int lvIndex = 0;
518 int samParametersLength = samType.parameterCount();
519 int captureArity = invokedType.parameterCount();
520 for (int i = 0; i < samParametersLength; i++) {
521 Class<?> argType = samType.parameterType(i);
522 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
523 lvIndex += getParameterSize(argType);
|