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