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