1 /*
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.
4 *
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.
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
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;
41 import java.util.Set;
42
43 import static jdk.internal.org.objectweb.asm.Opcodes.*;
44
45 /**
46 * Lambda metafactory implementation which dynamically creates an
47 * inner-class-like class per lambda callsite.
48 *
49 * @see LambdaMetafactory
50 */
51 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
52 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
53
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";
59
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
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 DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
81
82 private static final String[] EMPTY_STRING_ARRAY = new String[0];
83
84 // Used to ensure that each spun class name is unique
85 private static final AtomicInteger counter = new AtomicInteger(0);
86
87 // For dumping generated classes to disk, for debugging purposes
88 private static final ProxyClassesDumper dumper;
89
90 static {
91 final String key = "jdk.internal.lambda.dumpProxyClasses";
92 String path = GetPropertyAction.privilegedGetProperty(key);
93 dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
94 }
95
96 // See context values in AbstractValidatingLambdaMetafactory
97 private final String implMethodClassName; // Name of type containing implementation "CC"
98 private final String implMethodName; // Name of implementation method "impl"
99 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
100 private final MethodType constructorType; // Generated class constructor type "(CC)void"
101 private final ClassWriter cw; // ASM class writer
102 private final String[] argNames; // Generated names for the constructor arguments
103 private final String[] argDescs; // Type descriptors for the constructor arguments
104 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
105
106 /**
107 * General meta-factory constructor, supporting both standard cases and
108 * allowing for uncommon options such as serialization or bridging.
109 *
110 * @param caller Stacked automatically by VM; represents a lookup context
111 * with the accessibility privileges of the caller.
112 * @param invokedType Stacked automatically by VM; the signature of the
113 * invoked method, which includes the expected static
114 * type of the returned lambda object, and the static
115 * types of the captured arguments for the lambda. In
116 * the event that the implementation method is an
117 * instance method, the first argument in the invocation
118 * signature will correspond to the receiver.
119 * @param samMethodName Name of the method in the functional interface to
120 * which the lambda or method reference is being
121 * converted, represented as a String.
122 * @param samMethodType Type of the method in the functional interface to
123 * which the lambda or method reference is being
124 * converted, represented as a MethodType.
125 * @param implMethod The implementation method which should be called (with
126 * suitable adaptation of argument types, return types,
127 * and adjustment for captured arguments) when methods of
128 * the resulting functional interface instance are invoked.
129 * @param instantiatedMethodType The signature of the primary functional
130 * interface method after type variables are
131 * substituted with their instantiation from
132 * the capture site
133 * @param isSerializable Should the lambda be made serializable? If set,
134 * either the target type or one of the additional SAM
135 * types must extend {@code Serializable}.
136 * @param markerInterfaces Additional interfaces which the lambda object
137 * should implement.
138 * @param additionalBridges Method types for additional signatures to be
139 * bridged to the implementation method
140 * @throws LambdaConversionException If any of the meta-factory protocol
141 * invariants are violated
142 */
143 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
144 MethodType invokedType,
145 String samMethodName,
146 MethodType samMethodType,
147 MethodHandle implMethod,
148 MethodType instantiatedMethodType,
149 boolean isSerializable,
150 Class<?>[] markerInterfaces,
151 MethodType[] additionalBridges)
152 throws LambdaConversionException {
153 super(caller, invokedType, samMethodName, samMethodType,
154 implMethod, instantiatedMethodType,
155 isSerializable, markerInterfaces, additionalBridges);
156 implMethodClassName = implClass.getName().replace('.', '/');
157 implMethodName = implInfo.getName();
158 implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
159 constructorType = invokedType.changeReturnType(Void.TYPE);
160 lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
161 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
162 int parameterCount = invokedType.parameterCount();
163 if (parameterCount > 0) {
164 argNames = new String[parameterCount];
165 argDescs = new String[parameterCount];
166 for (int i = 0; i < parameterCount; i++) {
167 argNames[i] = "arg$" + (i + 1);
168 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
169 }
170 } else {
171 argNames = argDescs = EMPTY_STRING_ARRAY;
172 }
173 }
174
175 /**
176 * Build the CallSite. Generate a class file which implements the functional
177 * interface, define the class, if there are no parameters create an instance
178 * of the class which the CallSite will return, otherwise, generate handles
179 * which will call the class' constructor.
180 *
181 * @return a CallSite, which, when invoked, will return an instance of the
182 * functional interface
183 * @throws ReflectiveOperationException
184 * @throws LambdaConversionException If properly formed functional interface
185 * is not found
186 */
187 @Override
188 CallSite buildCallSite() throws LambdaConversionException {
189 final Class<?> innerClass = spinInnerClass();
190 if (invokedType.parameterCount() == 0) {
191 final Constructor<?>[] ctrs = AccessController.doPrivileged(
192 new PrivilegedAction<>() {
193 @Override
194 public Constructor<?>[] run() {
195 Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
196 if (ctrs.length == 1) {
197 // The lambda implementing inner class constructor is private, set
198 // it accessible (by us) before creating the constant sole instance
199 ctrs[0].setAccessible(true);
200 }
201 return ctrs;
202 }
203 });
204 if (ctrs.length != 1) {
205 throw new LambdaConversionException("Expected one lambda constructor for "
206 + innerClass.getCanonicalName() + ", got " + ctrs.length);
207 }
208
209 try {
210 Object inst = ctrs[0].newInstance();
211 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
212 }
213 catch (ReflectiveOperationException e) {
214 throw new LambdaConversionException("Exception instantiating lambda object", e);
215 }
216 } else {
217 try {
218 UNSAFE.ensureClassInitialized(innerClass);
219 return new ConstantCallSite(
220 MethodHandles.Lookup.IMPL_LOOKUP
221 .findStatic(innerClass, NAME_FACTORY, invokedType));
222 }
223 catch (ReflectiveOperationException e) {
224 throw new LambdaConversionException("Exception finding constructor", e);
225 }
226 }
227 }
228
229 /**
230 * Generate a class file which implements the functional
231 * interface, define and return the class.
232 *
233 * @implNote The class that is generated does not include signature
234 * information for exceptions that may be present on the SAM method.
235 * This is to reduce classfile size, and is harmless as checked exceptions
236 * are erased anyway, no one will ever compile against this classfile,
237 * and we make no guarantees about the reflective properties of lambda
238 * objects.
239 *
240 * @return a Class which implements the functional interface
241 * @throws LambdaConversionException If properly formed functional interface
242 * is not found
243 */
244 private Class<?> spinInnerClass() throws LambdaConversionException {
245 String[] interfaces;
246 String samIntf = samBase.getName().replace('.', '/');
247 boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
248 if (markerInterfaces.length == 0) {
249 interfaces = new String[]{samIntf};
250 } else {
251 // Assure no duplicate interfaces (ClassFormatError)
252 Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
253 itfs.add(samIntf);
254 for (Class<?> markerInterface : markerInterfaces) {
255 itfs.add(markerInterface.getName().replace('.', '/'));
256 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
257 }
258 interfaces = itfs.toArray(new String[itfs.size()]);
259 }
260
261 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
262 lambdaClassName, null,
263 JAVA_LANG_OBJECT, interfaces);
264
265 // Generate final fields to be filled in by constructor
266 for (int i = 0; i < argDescs.length; i++) {
267 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
268 argNames[i],
269 argDescs[i],
270 null, null);
271 fv.visitEnd();
272 }
273
274 generateConstructor();
275
276 if (invokedType.parameterCount() != 0) {
277 generateFactory();
278 }
279
280 // Forward the SAM method
281 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
282 samMethodType.toMethodDescriptorString(), null, null);
283 mv.visitAnnotation(DESCR_HIDDEN, true);
284 new ForwardingMethodGenerator(mv).generate(samMethodType);
285
286 // Forward the bridges
287 if (additionalBridges != null) {
288 for (MethodType mt : additionalBridges) {
289 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
290 mt.toMethodDescriptorString(), null, null);
291 mv.visitAnnotation(DESCR_HIDDEN, true);
292 new ForwardingMethodGenerator(mv).generate(mt);
293 }
294 }
295
296 if (isSerializable)
297 generateSerializationFriendlyMethods();
298 else if (accidentallySerializable)
299 generateSerializationHostileMethods();
300
301 cw.visitEnd();
302
303 // Define the generated class in this VM.
304
305 final byte[] classBytes = cw.toByteArray();
306
307 // If requested, dump out to a file for debugging purposes
308 if (dumper != null) {
309 AccessController.doPrivileged(new PrivilegedAction<>() {
310 @Override
311 public Void run() {
312 dumper.dumpClass(lambdaClassName, classBytes);
313 return null;
314 }
315 }, null,
316 new FilePermission("<<ALL FILES>>", "read, write"),
317 // createDirectories may need it
318 new PropertyPermission("user.dir", "read"));
319 }
320
321 return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
322 }
323
324 /**
325 * Generate the factory method for the class
326 */
327 private void generateFactory() {
328 MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
329 m.visitCode();
330 m.visitTypeInsn(NEW, lambdaClassName);
331 m.visitInsn(Opcodes.DUP);
332 int parameterCount = invokedType.parameterCount();
333 for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
334 Class<?> argType = invokedType.parameterType(typeIndex);
335 m.visitVarInsn(getLoadOpcode(argType), varIndex);
336 varIndex += getParameterSize(argType);
337 }
338 m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
339 m.visitInsn(ARETURN);
340 m.visitMaxs(-1, -1);
341 m.visitEnd();
342 }
343
344 /**
345 * Generate the constructor for the class
346 */
347 private void generateConstructor() {
348 // Generate constructor
349 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
350 constructorType.toMethodDescriptorString(), null, null);
351 ctor.visitCode();
352 ctor.visitVarInsn(ALOAD, 0);
353 ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
354 METHOD_DESCRIPTOR_VOID, false);
355 int parameterCount = invokedType.parameterCount();
356 for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
357 ctor.visitVarInsn(ALOAD, 0);
358 Class<?> argType = invokedType.parameterType(i);
359 ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
360 lvIndex += getParameterSize(argType);
361 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
362 }
363 ctor.visitInsn(RETURN);
364 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
365 ctor.visitMaxs(-1, -1);
366 ctor.visitEnd();
367 }
368
369 /**
370 * Generate a writeReplace method that supports serialization
371 */
372 private void generateSerializationFriendlyMethods() {
373 TypeConvertingMethodAdapter mv
374 = new TypeConvertingMethodAdapter(
375 cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
376 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
377 null, null));
378
379 mv.visitCode();
380 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
381 mv.visitInsn(DUP);
382 mv.visitLdcInsn(Type.getType(targetClass));
383 mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
384 mv.visitLdcInsn(samMethodName);
385 mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
386 mv.visitLdcInsn(implInfo.getReferenceKind());
387 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
388 mv.visitLdcInsn(implInfo.getName());
389 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
390 mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
391 mv.iconst(argDescs.length);
392 mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
393 for (int i = 0; i < argDescs.length; i++) {
394 mv.visitInsn(DUP);
395 mv.iconst(i);
396 mv.visitVarInsn(ALOAD, 0);
397 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
398 mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
399 mv.visitInsn(AASTORE);
400 }
401 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
402 DESCR_CTOR_SERIALIZED_LAMBDA, false);
403 mv.visitInsn(ARETURN);
404 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
405 mv.visitMaxs(-1, -1);
406 mv.visitEnd();
407 }
408
409 /**
410 * Generate a readObject/writeObject method that is hostile to serialization
411 */
412 private void generateSerializationHostileMethods() {
413 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
414 NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
415 null, SER_HOSTILE_EXCEPTIONS);
416 mv.visitCode();
417 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
418 mv.visitInsn(DUP);
419 mv.visitLdcInsn("Non-serializable lambda");
420 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
421 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
422 mv.visitInsn(ATHROW);
423 mv.visitMaxs(-1, -1);
424 mv.visitEnd();
425
426 mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
427 NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
428 null, SER_HOSTILE_EXCEPTIONS);
429 mv.visitCode();
430 mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
431 mv.visitInsn(DUP);
432 mv.visitLdcInsn("Non-serializable lambda");
433 mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
434 DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
435 mv.visitInsn(ATHROW);
436 mv.visitMaxs(-1, -1);
437 mv.visitEnd();
438 }
439
440 /**
441 * This class generates a method body which calls the lambda implementation
442 * method, converting arguments, as needed.
443 */
444 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
445
446 ForwardingMethodGenerator(MethodVisitor mv) {
447 super(mv);
448 }
449
450 void generate(MethodType methodType) {
451 visitCode();
452
453 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
454 visitTypeInsn(NEW, implMethodClassName);
455 visitInsn(DUP);
456 }
457 for (int i = 0; i < argNames.length; i++) {
458 visitVarInsn(ALOAD, 0);
459 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
460 }
461
462 convertArgumentTypes(methodType);
463
464 // Invoke the method we want to forward to
465 visitMethodInsn(invocationOpcode(), implMethodClassName,
466 implMethodName, implMethodDesc,
467 implClass.isInterface());
468
469 // Convert the return value (if any) and return it
470 // Note: if adapting from non-void to void, the 'return'
471 // instruction will pop the unneeded result
472 Class<?> implReturnClass = implMethodType.returnType();
473 Class<?> samReturnClass = methodType.returnType();
474 convertType(implReturnClass, samReturnClass, samReturnClass);
475 visitInsn(getReturnOpcode(samReturnClass));
476 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
477 visitMaxs(-1, -1);
478 visitEnd();
479 }
480
481 private void convertArgumentTypes(MethodType samType) {
482 int lvIndex = 0;
483 int samParametersLength = samType.parameterCount();
484 int captureArity = invokedType.parameterCount();
485 for (int i = 0; i < samParametersLength; i++) {
486 Class<?> argType = samType.parameterType(i);
487 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
488 lvIndex += getParameterSize(argType);
489 convertType(argType, implMethodType.parameterType(captureArity + i), instantiatedMethodType.parameterType(i));
490 }
491 }
492
493 private int invocationOpcode() throws InternalError {
494 switch (implKind) {
495 case MethodHandleInfo.REF_invokeStatic:
496 return INVOKESTATIC;
497 case MethodHandleInfo.REF_newInvokeSpecial:
498 return INVOKESPECIAL;
499 case MethodHandleInfo.REF_invokeVirtual:
500 return INVOKEVIRTUAL;
501 case MethodHandleInfo.REF_invokeInterface:
502 return INVOKEINTERFACE;
503 case MethodHandleInfo.REF_invokeSpecial:
504 return INVOKESPECIAL;
505 default:
506 throw new InternalError("Unexpected invocation kind: " + implKind);
507 }
508 }
509 }
510
511 static int getParameterSize(Class<?> c) {
512 if (c == Void.TYPE) {
513 return 0;
514 } else if (c == Long.TYPE || c == Double.TYPE) {
515 return 2;
516 }
517 return 1;
518 }
519
520 static int getLoadOpcode(Class<?> c) {
521 if(c == Void.TYPE) {
522 throw new InternalError("Unexpected void type of load opcode");
523 }
524 return ILOAD + getOpcodeOffset(c);
525 }
526
527 static int getReturnOpcode(Class<?> c) {
528 if(c == Void.TYPE) {
529 return RETURN;
530 }
531 return IRETURN + getOpcodeOffset(c);
532 }
533
534 private static int getOpcodeOffset(Class<?> c) {
535 if (c.isPrimitive()) {
536 if (c == Long.TYPE) {
537 return 1;
538 } else if (c == Float.TYPE) {
539 return 2;
540 } else if (c == Double.TYPE) {
541 return 3;
542 }
543 return 0;
544 } else {
545 return 4;
546 }
547 }
548
549 }