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