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