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