1 /* 2 * Copyright (c) 2012, 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.misc.Unsafe; 30 31 import java.lang.reflect.Constructor; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.security.ProtectionDomain; 35 import java.util.concurrent.atomic.AtomicInteger; 36 37 import static jdk.internal.org.objectweb.asm.Opcodes.*; 38 39 /** 40 * Lambda metafactory implementation which dynamically creates an 41 * inner-class-like class per lambda callsite. 42 * 43 * @see LambdaMetafactory 44 */ 45 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { 46 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); 47 48 private static final int CLASSFILE_VERSION = 51; 49 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE); 50 private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl"; 51 private static final String NAME_CTOR = "<init>"; 52 private static final String NAME_FACTORY = "get$Lambda"; 53 54 //Serialization support 55 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda"; 56 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; 57 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; 58 private static final String NAME_OBJECT = "java/lang/Object"; 59 private static final String DESCR_CTOR_SERIALIZED_LAMBDA 60 = MethodType.methodType(void.class, 61 Class.class, 62 String.class, String.class, String.class, 63 int.class, String.class, String.class, String.class, 64 String.class, 65 Object[].class).toMethodDescriptorString(); 66 67 // Used to ensure that each spun class name is unique 68 private static final AtomicInteger counter = new AtomicInteger(0); 69 70 // See context values in AbstractValidatingLambdaMetafactory 71 private final String implMethodClassName; // Name of type containing implementation "CC" 72 private final String implMethodName; // Name of implementation method "impl" 73 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" 74 private final Type[] implMethodArgumentTypes; // ASM types for implementaion method parameters 75 private final Type implMethodReturnType; // ASM type for implementaion method return type "Ljava/lang/String;" 76 private final MethodType constructorType; // Generated class constructor type "(CC)void" 77 private final String constructorDesc; // Type descriptor for constructor "(LCC;)V" 78 private final ClassWriter cw; // ASM class writer 79 private final Type[] argTypes; // ASM types for the constructor arguments 80 private final String[] argNames; // Generated names for the constructor arguments 81 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" 82 private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments 83 84 /** 85 * General meta-factory constructor, supporting both standard cases and 86 * allowing for uncommon options such as serialization or bridging. 87 * 88 * @param caller Stacked automatically by VM; represents a lookup context 89 * with the accessibility privileges of the caller. 90 * @param invokedType Stacked automatically by VM; the signature of the 91 * invoked method, which includes the expected static 92 * type of the returned lambda object, and the static 93 * types of the captured arguments for the lambda. In 94 * the event that the implementation method is an 95 * instance method, the first argument in the invocation 96 * signature will correspond to the receiver. 97 * @param samMethodName Name of the method in the functional interface to 98 * which the lambda or method reference is being 99 * converted, represented as a String. 100 * @param samMethodType Type of the method in the functional interface to 101 * which the lambda or method reference is being 102 * converted, represented as a MethodType. 103 * @param implMethod The implementation method which should be called (with 104 * suitable adaptation of argument types, return types, 105 * and adjustment for captured arguments) when methods of 106 * the resulting functional interface instance are invoked. 107 * @param instantiatedMethodType The signature of the primary functional 108 * interface method after type variables are 109 * substituted with their instantiation from 110 * the capture site 111 * @param isSerializable Should the lambda be made serializable? If set, 112 * either the target type or one of the additional SAM 113 * types must extend {@code Serializable}. 114 * @param markerInterfaces Additional interfaces which the lambda object 115 * should implement. 116 * @param additionalBridges Method types for additional signatures to be 117 * bridged to the implementation method 118 * @throws ReflectiveOperationException 119 * @throws LambdaConversionException If any of the meta-factory protocol 120 * invariants are violated 121 */ 122 public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, 123 MethodType invokedType, 124 String samMethodName, 125 MethodType samMethodType, 126 MethodHandle implMethod, 127 MethodType instantiatedMethodType, 128 boolean isSerializable, 129 Class<?>[] markerInterfaces, 130 MethodType[] additionalBridges) 131 throws ReflectiveOperationException, LambdaConversionException { 132 super(caller, invokedType, samMethodName, samMethodType, 133 implMethod, instantiatedMethodType, 134 isSerializable, markerInterfaces, additionalBridges); 135 implMethodClassName = implDefiningClass.getName().replace('.', '/'); 136 implMethodName = implInfo.getName(); 137 implMethodDesc = implMethodType.toMethodDescriptorString(); 138 Type implMethodAsmType = Type.getMethodType(implMethodDesc); 139 implMethodArgumentTypes = implMethodAsmType.getArgumentTypes(); 140 implMethodReturnType = (implKind == MethodHandleInfo.REF_newInvokeSpecial) 141 ? Type.getObjectType(implMethodClassName) 142 : implMethodAsmType.getReturnType(); 143 constructorType = invokedType.changeReturnType(Void.TYPE); 144 constructorDesc = constructorType.toMethodDescriptorString(); 145 lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet(); 146 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 147 argTypes = Type.getArgumentTypes(constructorDesc); 148 argNames = new String[argTypes.length]; 149 for (int i = 0; i < argTypes.length; i++) { 150 argNames[i] = "arg$" + (i + 1); 151 } 152 instantiatedArgumentTypes = Type.getArgumentTypes( 153 instantiatedMethodType.toMethodDescriptorString()); 154 } 155 156 /** 157 * Build the CallSite. Generate a class file which implements the functional 158 * interface, define the class, if there are no parameters create an instance 159 * of the class which the CallSite will return, otherwise, generate handles 160 * which will call the class' constructor. 161 * 162 * @return a CallSite, which, when invoked, will return an instance of the 163 * functional interface 164 * @throws ReflectiveOperationException 165 * @throws LambdaConversionException If properly formed functional interface 166 * is not found 167 */ 168 @Override 169 CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException { 170 final Class<?> innerClass = spinInnerClass(); 171 if (invokedType.parameterCount() == 0) { 172 final Constructor[] ctrs = AccessController.doPrivileged( 173 new PrivilegedAction<Constructor[]>() { 174 @Override 175 public Constructor[] run() { 176 return innerClass.getDeclaredConstructors(); 177 } 178 }); 179 if (ctrs.length != 1) { 180 throw new ReflectiveOperationException("Expected one lambda constructor for " 181 + innerClass.getCanonicalName() + ", got " + ctrs.length); 182 } 183 // The lambda implementing inner class constructor is private, set 184 // it accessible (by us) before creating the constant sole instance 185 AccessController.doPrivileged(new PrivilegedAction<Void>() { 186 @Override 187 public Void run() { 188 ctrs[0].setAccessible(true); 189 return null; 190 } 191 }); 192 Object inst = ctrs[0].newInstance(); 193 return new ConstantCallSite(MethodHandles.constant(samBase, inst)); 194 } else { 195 return new ConstantCallSite( 196 MethodHandles.Lookup.IMPL_LOOKUP.findStatic(innerClass, NAME_FACTORY, invokedType)); 197 } 198 } 199 200 /** 201 * Generate a class file which implements the functional 202 * interface, define and return the class. 203 * 204 * @implNote The class that is generated does not include signature 205 * information for exceptions that may be present on the SAM method. 206 * This is to reduce classfile size, and is harmless as checked exceptions 207 * are erased anyway, no one will ever compile against this classfile, 208 * and we make no guarantees about the reflective properties of lambda 209 * objects. 210 * 211 * @return a Class which implements the functional interface 212 * @throws LambdaConversionException If properly formed functional interface 213 * is not found 214 */ 215 private Class<?> spinInnerClass() throws LambdaConversionException { 216 String[] interfaces = new String[markerInterfaces.length + 1]; 217 interfaces[0] = samBase.getName().replace('.', '/'); 218 for (int i=0; i<markerInterfaces.length; i++) { 219 interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/'); 220 } 221 cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, 222 lambdaClassName, null, 223 NAME_MAGIC_ACCESSOR_IMPL, interfaces); 224 225 // Generate final fields to be filled in by constructor 226 for (int i = 0; i < argTypes.length; i++) { 227 FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, 228 argNames[i], 229 argTypes[i].getDescriptor(), 230 null, null); 231 fv.visitEnd(); 232 } 233 234 generateConstructor(); 235 236 if (invokedType.parameterCount() != 0) { 237 generateFactory(); 238 } 239 240 // Forward the SAM method 241 String methodDescriptor = samMethodType.toMethodDescriptorString(); 242 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, 243 methodDescriptor, null, null); 244 new ForwardingMethodGenerator(mv).generate(methodDescriptor); 245 246 // Forward the bridges 247 if (additionalBridges != null) { 248 for (MethodType mt : additionalBridges) { 249 methodDescriptor = mt.toMethodDescriptorString(); 250 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, 251 methodDescriptor, null, null); 252 new ForwardingMethodGenerator(mv).generate(methodDescriptor); 253 } 254 } 255 256 if (isSerializable) 257 generateWriteReplace(); 258 259 cw.visitEnd(); 260 261 // Define the generated class in this VM. 262 263 final byte[] classBytes = cw.toByteArray(); 264 265 /*** Uncomment to dump the generated file 266 System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, 267 classBytes.length); 268 try (FileOutputStream fos = new FileOutputStream(lambdaClassName 269 .replace('/', '.') + ".class")) { 270 fos.write(classBytes); 271 } catch (IOException ex) { 272 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class 273 .getName()).severe(ex.getMessage(), ex); 274 } 275 ***/ 276 277 ClassLoader loader = targetClass.getClassLoader(); 278 ProtectionDomain pd = (loader == null) 279 ? null 280 : AccessController.doPrivileged( 281 new PrivilegedAction<ProtectionDomain>() { 282 @Override 283 public ProtectionDomain run() { 284 return targetClass.getProtectionDomain(); 285 } 286 } 287 ); 288 289 return UNSAFE.defineClass(lambdaClassName, 290 classBytes, 0, classBytes.length, 291 loader, pd); 292 } 293 294 /** 295 * Generate the factory method for the class 296 */ 297 private void generateFactory() { 298 MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null); 299 m.visitCode(); 300 m.visitTypeInsn(NEW, lambdaClassName); 301 m.visitInsn(Opcodes.DUP); 302 for (int typeIndex = 0, varIndex = 0; typeIndex < argTypes.length; typeIndex++) { 303 m.visitVarInsn(argTypes[typeIndex].getOpcode(ILOAD), varIndex); 304 varIndex += argTypes[typeIndex].getSize(); 305 } 306 m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorDesc); 307 m.visitInsn(ARETURN); 308 m.visitMaxs(-1, -1); 309 m.visitEnd(); 310 } 311 312 /** 313 * Generate the constructor for the class 314 */ 315 private void generateConstructor() { 316 // Generate constructor 317 MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, 318 constructorDesc, null, null); 319 ctor.visitCode(); 320 ctor.visitVarInsn(ALOAD, 0); 321 ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, 322 METHOD_DESCRIPTOR_VOID); 323 int lvIndex = 0; 324 for (int i = 0; i < argTypes.length; i++) { 325 ctor.visitVarInsn(ALOAD, 0); 326 ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1); 327 lvIndex += argTypes[i].getSize(); 328 ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], 329 argTypes[i].getDescriptor()); 330 } 331 ctor.visitInsn(RETURN); 332 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 333 ctor.visitMaxs(-1, -1); 334 ctor.visitEnd(); 335 } 336 337 /** 338 * Generate the writeReplace method (if needed for serialization) 339 */ 340 private void generateWriteReplace() { 341 TypeConvertingMethodAdapter mv 342 = new TypeConvertingMethodAdapter( 343 cw.visitMethod(ACC_PRIVATE + ACC_FINAL, 344 NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, 345 null, null)); 346 347 mv.visitCode(); 348 mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA); 349 mv.visitInsn(DUP); 350 mv.visitLdcInsn(Type.getType(targetClass)); 351 mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/')); 352 mv.visitLdcInsn(samMethodName); 353 mv.visitLdcInsn(samMethodType.toMethodDescriptorString()); 354 mv.visitLdcInsn(implInfo.getReferenceKind()); 355 mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/')); 356 mv.visitLdcInsn(implInfo.getName()); 357 mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString()); 358 mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString()); 359 360 mv.iconst(argTypes.length); 361 mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT); 362 for (int i = 0; i < argTypes.length; i++) { 363 mv.visitInsn(DUP); 364 mv.iconst(i); 365 mv.visitVarInsn(ALOAD, 0); 366 mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], 367 argTypes[i].getDescriptor()); 368 mv.boxIfTypePrimitive(argTypes[i]); 369 mv.visitInsn(AASTORE); 370 } 371 mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR, 372 DESCR_CTOR_SERIALIZED_LAMBDA); 373 mv.visitInsn(ARETURN); 374 // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored 375 mv.visitMaxs(-1, -1); 376 mv.visitEnd(); 377 } 378 379 /** 380 * This class generates a method body which calls the lambda implementation 381 * method, converting arguments, as needed. 382 */ 383 private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { 384 385 ForwardingMethodGenerator(MethodVisitor mv) { 386 super(mv); 387 } 388 389 void generate(String methodDescriptor) { 390 visitCode(); 391 392 if (implKind == MethodHandleInfo.REF_newInvokeSpecial) { 393 visitTypeInsn(NEW, implMethodClassName); 394 visitInsn(DUP); 395 } 396 for (int i = 0; i < argTypes.length; i++) { 397 visitVarInsn(ALOAD, 0); 398 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], 399 argTypes[i].getDescriptor()); 400 } 401 402 convertArgumentTypes(Type.getArgumentTypes(methodDescriptor)); 403 404 // Invoke the method we want to forward to 405 visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc); 406 407 // Convert the return value (if any) and return it 408 // Note: if adapting from non-void to void, the 'return' 409 // instruction will pop the unneeded result 410 Type samReturnType = Type.getReturnType(methodDescriptor); 411 convertType(implMethodReturnType, samReturnType, samReturnType); 412 visitInsn(samReturnType.getOpcode(Opcodes.IRETURN)); 413 // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored 414 visitMaxs(-1, -1); 415 visitEnd(); 416 } 417 418 private void convertArgumentTypes(Type[] samArgumentTypes) { 419 int lvIndex = 0; 420 boolean samIncludesReceiver = implIsInstanceMethod && 421 argTypes.length == 0; 422 int samReceiverLength = samIncludesReceiver ? 1 : 0; 423 if (samIncludesReceiver) { 424 // push receiver 425 Type rcvrType = samArgumentTypes[0]; 426 Type instantiatedRcvrType = instantiatedArgumentTypes[0]; 427 428 visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1); 429 lvIndex += rcvrType.getSize(); 430 convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType); 431 } 432 int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length; 433 for (int i = samReceiverLength; i < samArgumentTypes.length; i++) { 434 Type argType = samArgumentTypes[i]; 435 Type targetType = implMethodArgumentTypes[argOffset + i]; 436 Type instantiatedArgType = instantiatedArgumentTypes[i]; 437 438 visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1); 439 lvIndex += argType.getSize(); 440 convertType(argType, targetType, instantiatedArgType); 441 } 442 } 443 444 private void convertType(Type argType, Type targetType, Type functionalType) { 445 convertType(argType.getDescriptor(), 446 targetType.getDescriptor(), 447 functionalType.getDescriptor()); 448 } 449 450 private int invocationOpcode() throws InternalError { 451 switch (implKind) { 452 case MethodHandleInfo.REF_invokeStatic: 453 return INVOKESTATIC; 454 case MethodHandleInfo.REF_newInvokeSpecial: 455 return INVOKESPECIAL; 456 case MethodHandleInfo.REF_invokeVirtual: 457 return INVOKEVIRTUAL; 458 case MethodHandleInfo.REF_invokeInterface: 459 return INVOKEINTERFACE; 460 case MethodHandleInfo.REF_invokeSpecial: 461 return INVOKESPECIAL; 462 default: 463 throw new InternalError("Unexpected invocation kind: " + implKind); 464 } 465 } 466 } 467 }