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