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