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