Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
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.
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.
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).
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.
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
26 package java.lang.invoke;
28 import sun.invoke.util.VerifyAccess;
29 import java.lang.invoke.LambdaForm.Name;
30 import java.lang.invoke.MethodHandles.Lookup;
32 import sun.invoke.util.Wrapper;
37 import jdk.internal.org.objectweb.asm.*;
39 import java.lang.reflect.*;
40 import static java.lang.invoke.MethodHandleStatics.*;
41 import static java.lang.invoke.MethodHandleNatives.Constants.*;
42 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
43 import sun.invoke.util.ValueConversions;
44 import sun.invoke.util.VerifyType;
47 * Code generation backend for LambdaForm.
49 * @author John Rose, JSR 292 EG
51 class InvokerBytecodeGenerator {
52 /** Define class names for convenience. */
53 private static final String MH = "java/lang/invoke/MethodHandle";
54 private static final String BMH = "java/lang/invoke/BoundMethodHandle";
55 private static final String LF = "java/lang/invoke/LambdaForm";
56 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
57 private static final String CLS = "java/lang/Class";
58 private static final String OBJ = "java/lang/Object";
59 private static final String OBJARY = "[Ljava/lang/Object;";
61 private static final String LF_SIG = "L" + LF + ";";
62 private static final String LFN_SIG = "L" + LFN + ";";
63 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
65 /** Name of its super class*/
66 private static final String superName = LF;
68 /** Name of new class */
69 private final String className;
71 /** Name of the source file (for stack trace printing). */
72 private final String sourceFile;
74 private final LambdaForm lambdaForm;
75 private final String invokerName;
76 private final MethodType invokerType;
77 private final int[] localsMap;
79 /** ASM bytecode generation. */
80 private ClassWriter cw;
81 private MethodVisitor mv;
83 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
84 private static final Class<?> HOST_CLASS = LambdaForm.class;
86 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
87 String className, String invokerName, MethodType invokerType) {
88 if (invokerName.contains(".")) {
89 int p = invokerName.indexOf(".");
90 className = invokerName.substring(0, p);
91 invokerName = invokerName.substring(p+1);
93 if (DUMP_CLASS_FILES) {
94 className = makeDumpableClassName(className);
96 this.className = superName + "$" + className;
97 this.sourceFile = "LambdaForm$" + className;
98 this.lambdaForm = lambdaForm;
99 this.invokerName = invokerName;
100 this.invokerType = invokerType;
101 this.localsMap = new int[localsMapSize];
104 private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
105 this(null, invokerType.parameterCount(),
106 className, invokerName, invokerType);
107 // Create an array to map name indexes to locals indexes.
108 for (int i = 0; i < localsMap.length; i++) {
109 localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
113 private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
114 this(form, form.names.length,
115 className, form.debugName, invokerType);
116 // Create an array to map name indexes to locals indexes.
117 Name[] names = form.names;
118 for (int i = 0, index = 0; i < localsMap.length; i++) {
119 localsMap[i] = index;
120 index += Wrapper.forBasicType(names[i].type).stackSlots();
125 /** instance counters for dumped classes */
126 private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
127 /** debugging flag for saving generated class files */
128 private final static File DUMP_CLASS_FILES_DIR;
131 if (DUMP_CLASS_FILES) {
132 DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
134 File dumpDir = new File("DUMP_CLASS_FILES");
135 if (!dumpDir.exists()) {
138 DUMP_CLASS_FILES_DIR = dumpDir;
139 System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
140 } catch (Exception e) {
141 throw newInternalError(e);
144 DUMP_CLASS_FILES_COUNTERS = null;
145 DUMP_CLASS_FILES_DIR = null;
149 static void maybeDump(final String className, final byte[] classFile) {
150 if (DUMP_CLASS_FILES) {
151 System.out.println("dump: " + className);
152 java.security.AccessController.doPrivileged(
153 new java.security.PrivilegedAction<Void>() {
156 String dumpName = className;
157 //dumpName = dumpName.replace('/', '-');
158 File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
159 dumpFile.getParentFile().mkdirs();
160 FileOutputStream file = new FileOutputStream(dumpFile);
161 file.write(classFile);
164 } catch (IOException ex) {
165 throw newInternalError(ex);
173 private static String makeDumpableClassName(String className) {
175 synchronized (DUMP_CLASS_FILES_COUNTERS) {
176 ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
177 if (ctr == null) ctr = 0;
178 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
180 String sfx = ctr.toString();
181 while (sfx.length() < 3)
189 final String placeholder;
191 CpPatch(int index, String placeholder, Object value) {
193 this.placeholder = placeholder;
196 public String toString() {
197 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
201 Map<Object, CpPatch> cpPatches = new HashMap<>();
203 int cph = 0; // for counting constant placeholders
205 String constantPlaceholder(Object arg) {
206 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
207 if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid
208 if (cpPatches.containsKey(cpPlaceholder)) {
209 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
211 // insert placeholder in CP and remember the patch
212 int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool
213 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg));
214 return cpPlaceholder;
217 Object[] cpPatches(byte[] classFile) {
218 int size = getConstantPoolSize(classFile);
219 Object[] res = new Object[size];
220 for (CpPatch p : cpPatches.values()) {
222 throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
223 res[p.index] = p.value;
229 * Extract the number of constant pool entries from a given class file.
231 * @param classFile the bytes of the class file in question.
232 * @return the number of entries in the constant pool.
234 private static int getConstantPoolSize(byte[] classFile) {
235 // The first few bytes:
239 // u2 constant_pool_count;
240 return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
244 * Extract the MemberName of a newly-defined method.
246 private MemberName loadMethod(byte[] classFile) {
247 Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
248 return resolveInvokerMember(invokerClass, invokerName, invokerType);
252 * Define a given class as anonymous class in the runtime system.
254 private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
255 Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
256 UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
260 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
261 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
262 //System.out.println("resolveInvokerMember => "+member);
263 //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
265 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
266 } catch (ReflectiveOperationException e) {
267 throw newInternalError(e);
269 //System.out.println("resolveInvokerMember => "+member);
274 * Set up class file generation.
276 private void classFilePrologue() {
277 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
278 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
279 cw.visitSource(sourceFile, null);
281 String invokerDesc = invokerType.toMethodDescriptorString();
282 mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
286 * Tear down class file generation.
288 private void classFileEpilogue() {
294 * Low-level emit helpers.
296 private void emitConst(Object con) {
298 mv.visitInsn(Opcodes.ACONST_NULL);
301 if (con instanceof Integer) {
302 emitIconstInsn((int) con);
305 if (con instanceof Long) {
307 if (x == (short) x) {
308 emitIconstInsn((int) x);
309 mv.visitInsn(Opcodes.I2L);
313 if (con instanceof Float) {
314 float x = (float) con;
315 if (x == (short) x) {
316 emitIconstInsn((int) x);
317 mv.visitInsn(Opcodes.I2F);
321 if (con instanceof Double) {
322 double x = (double) con;
323 if (x == (short) x) {
324 emitIconstInsn((int) x);
325 mv.visitInsn(Opcodes.I2D);
329 if (con instanceof Boolean) {
330 emitIconstInsn((boolean) con ? 1 : 0);
334 mv.visitLdcInsn(con);
337 private void emitIconstInsn(int i) {
340 case 0: opcode = Opcodes.ICONST_0; break;
341 case 1: opcode = Opcodes.ICONST_1; break;
342 case 2: opcode = Opcodes.ICONST_2; break;
343 case 3: opcode = Opcodes.ICONST_3; break;
344 case 4: opcode = Opcodes.ICONST_4; break;
345 case 5: opcode = Opcodes.ICONST_5; break;
348 mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
349 } else if (i == (short) i) {
350 mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
356 mv.visitInsn(opcode);
360 * NOTE: These load/store methods use the localsMap to find the correct index!
362 private void emitLoadInsn(char type, int index) {
365 case 'I': opcode = Opcodes.ILOAD; break;
366 case 'J': opcode = Opcodes.LLOAD; break;
367 case 'F': opcode = Opcodes.FLOAD; break;
368 case 'D': opcode = Opcodes.DLOAD; break;
369 case 'L': opcode = Opcodes.ALOAD; break;
371 throw new InternalError("unknown type: " + type);
373 mv.visitVarInsn(opcode, localsMap[index]);
375 private void emitAloadInsn(int index) {
376 emitLoadInsn('L', index);
379 private void emitStoreInsn(char type, int index) {
382 case 'I': opcode = Opcodes.ISTORE; break;
383 case 'J': opcode = Opcodes.LSTORE; break;
384 case 'F': opcode = Opcodes.FSTORE; break;
385 case 'D': opcode = Opcodes.DSTORE; break;
386 case 'L': opcode = Opcodes.ASTORE; break;
388 throw new InternalError("unknown type: " + type);
390 mv.visitVarInsn(opcode, localsMap[index]);
392 private void emitAstoreInsn(int index) {
393 emitStoreInsn('L', index);
397 * Emit a boxing call.
399 * @param type primitive type class to box.
401 private void emitBoxing(Class<?> type) {
402 Wrapper wrapper = Wrapper.forPrimitiveType(type);
403 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
404 String name = "valueOf";
405 String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
406 mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
410 * Emit an unboxing call (plus preceding checkcast).
412 * @param type wrapper type class to unbox.
414 private void emitUnboxing(Class<?> type) {
415 Wrapper wrapper = Wrapper.forWrapperType(type);
416 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
417 String name = wrapper.primitiveSimpleName() + "Value";
418 String desc = "()" + wrapper.basicTypeChar();
419 mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
420 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
424 * Emit an implicit conversion.
426 * @param ptype type of value present on stack
427 * @param pclass type of value required on stack
429 private void emitImplicitConversion(char ptype, Class<?> pclass) {
432 if (VerifyType.isNullConversion(Object.class, pclass))
434 if (isStaticallyNameable(pclass)) {
435 mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
437 mv.visitLdcInsn(constantPlaceholder(pclass));
438 mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
439 mv.visitInsn(Opcodes.SWAP);
440 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
441 if (pclass.isArray())
442 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
446 if (!VerifyType.isNullConversion(int.class, pclass))
447 emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
450 assert(pclass == long.class);
453 assert(pclass == float.class);
456 assert(pclass == double.class);
459 throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass);
463 * Emits an actual return instruction conforming to the given return type.
465 private void emitReturnInsn(Class<?> type) {
467 switch (Wrapper.basicTypeChar(type)) {
468 case 'I': opcode = Opcodes.IRETURN; break;
469 case 'J': opcode = Opcodes.LRETURN; break;
470 case 'F': opcode = Opcodes.FRETURN; break;
471 case 'D': opcode = Opcodes.DRETURN; break;
472 case 'L': opcode = Opcodes.ARETURN; break;
473 case 'V': opcode = Opcodes.RETURN; break;
475 throw new InternalError("unknown return type: " + type);
477 mv.visitInsn(opcode);
480 private static String getInternalName(Class<?> c) {
481 assert(VerifyAccess.isTypeVisible(c, Object.class));
482 return c.getName().replace('.', '/');
486 * Generate customized bytecode for a given LambdaForm.
488 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
489 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
490 return g.loadMethod(g.generateCustomizedCodeBytes());
494 * Generate an invoker method for the passed {@link LambdaForm}.
496 private byte[] generateCustomizedCodeBytes() {
499 // Suppress this method in backtraces displayed to the user.
500 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
502 // Mark this method as a compiled LambdaForm
503 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
505 // Force inlining of this invoker method.
506 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
508 // iterate over the form's names, generating bytecode instructions for each
509 // start iterating at the first name following the arguments
510 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
511 Name name = lambdaForm.names[i];
512 MemberName member = name.function.member();
514 if (isSelectAlternative(member)) {
515 // selectAlternative idiom
516 // FIXME: make sure this idiom is really present!
517 emitSelectAlternative(name, lambdaForm.names[i + 1]);
518 i++; // skip MH.invokeBasic of the selectAlternative result
519 } else if (isStaticallyInvocable(member)) {
520 emitStaticInvoke(member, name);
525 // store the result from evaluating to the target name in a local if required
526 // (if this is the last value, i.e., the one that is going to be returned,
527 // avoid store/load/return and just return)
528 if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
529 // return value - do nothing
530 } else if (name.type != 'V') {
531 // non-void: actually assign
532 emitStoreInsn(name.type, name.index());
540 bogusMethod(lambdaForm);
542 final byte[] classFile = cw.toByteArray();
543 maybeDump(className, classFile);
548 * Emit an invoke for the given name.
550 void emitInvoke(Name name) {
553 MethodHandle target = name.function.resolvedHandle;
554 assert(target != null) : name.exprString();
555 mv.visitLdcInsn(constantPlaceholder(target));
556 mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
560 mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
561 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
562 mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
567 for (int i = 0; i < name.arguments.length; i++) {
568 emitPushArgument(name, i);
572 MethodType type = name.function.methodType();
573 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
576 static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
577 // Sample classes from each package we are willing to bind to statically:
578 java.lang.Object.class,
579 java.util.Arrays.class,
580 sun.misc.Unsafe.class
581 //MethodHandle.class already covered
584 static boolean isStaticallyInvocable(MemberName member) {
585 if (member == null) return false;
586 if (member.isConstructor()) return false;
587 Class<?> cls = member.getDeclaringClass();
588 if (cls.isArray() || cls.isPrimitive())
589 return false; // FIXME
590 if (cls.isAnonymousClass() || cls.isLocalClass())
591 return false; // inner class of some sort
592 if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
593 return false; // not on BCP
594 MethodType mtype = member.getMethodOrFieldType();
595 if (!isStaticallyNameable(mtype.returnType()))
597 for (Class<?> ptype : mtype.parameterArray())
598 if (!isStaticallyNameable(ptype))
600 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
601 return true; // in java.lang.invoke package
602 if (member.isPublic() && isStaticallyNameable(cls))
607 static boolean isStaticallyNameable(Class<?> cls) {
608 while (cls.isArray())
609 cls = cls.getComponentType();
610 if (cls.isPrimitive())
611 return true; // int[].class, for example
612 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
613 if (cls.getClassLoader() != Object.class.getClassLoader())
615 if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
617 if (!Modifier.isPublic(cls.getModifiers()))
619 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
620 if (VerifyAccess.isSamePackage(pkgcls, cls))
627 * Emit an invoke for the given name, using the MemberName directly.
629 void emitStaticInvoke(MemberName member, Name name) {
630 assert(member.equals(name.function.member()));
631 String cname = getInternalName(member.getDeclaringClass());
632 String mname = member.getName();
634 byte refKind = member.getReferenceKind();
635 if (refKind == REF_invokeSpecial) {
636 // in order to pass the verifier, we need to convert this to invokevirtual in all cases
637 assert(member.canBeStaticallyBound()) : member;
638 refKind = REF_invokeVirtual;
641 if (member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual) {
642 // Methods from Object declared in an interface can be resolved by JVM to invokevirtual kind.
643 // Need to convert it back to invokeinterface to pass verification and make the invocation works as expected.
644 refKind = REF_invokeInterface;
648 for (int i = 0; i < name.arguments.length; i++) {
649 emitPushArgument(name, i);
653 if (member.isMethod()) {
654 mtype = member.getMethodType().toMethodDescriptorString();
655 mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype,
656 member.getDeclaringClass().isInterface());
658 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
659 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
662 int refKindOpcode(byte refKind) {
664 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
665 case REF_invokeStatic: return Opcodes.INVOKESTATIC;
666 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
667 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
668 case REF_getField: return Opcodes.GETFIELD;
669 case REF_putField: return Opcodes.PUTFIELD;
670 case REF_getStatic: return Opcodes.GETSTATIC;
671 case REF_putStatic: return Opcodes.PUTSTATIC;
673 throw new InternalError("refKind="+refKind);
677 * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
679 private boolean isSelectAlternative(MemberName member) {
680 return member != null &&
681 member.getDeclaringClass() == MethodHandleImpl.class &&
682 member.getName().equals("selectAlternative");
686 * Emit bytecode for the selectAlternative idiom.
688 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
689 * <blockquote><pre>{@code
690 * Lambda(a0:L,a1:I)=>{
691 * t2:I=foo.test(a1:I);
692 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
693 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
694 * }</pre></blockquote>
696 private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
697 MethodType type = selectAlternativeName.function.methodType();
699 Name receiver = (Name) invokeBasicName.arguments[0];
701 Label L_fallback = new Label();
702 Label L_done = new Label();
705 emitPushArgument(selectAlternativeName, 0);
706 mv.visitInsn(Opcodes.ICONST_1);
708 // if_icmpne L_fallback
709 mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
711 // invoke selectAlternativeName.arguments[1]
712 MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
713 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
714 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
715 emitInvoke(invokeBasicName);
718 mv.visitJumpInsn(Opcodes.GOTO, L_done);
721 mv.visitLabel(L_fallback);
723 // invoke selectAlternativeName.arguments[2]
724 MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
725 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
726 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
727 emitInvoke(invokeBasicName);
730 mv.visitLabel(L_done);
733 private void emitPushArgument(Name name, int paramIndex) {
734 Object arg = name.arguments[paramIndex];
735 char ptype = name.function.parameterType(paramIndex);
736 MethodType mtype = name.function.methodType();
737 if (arg instanceof Name) {
739 emitLoadInsn(n.type, n.index());
740 emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
741 } else if ((arg == null || arg instanceof String) && ptype == 'L') {
744 if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
747 mv.visitLdcInsn(constantPlaceholder(arg));
748 emitImplicitConversion('L', mtype.parameterType(paramIndex));
754 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
756 private void emitReturn() {
758 if (lambdaForm.result == -1) {
760 mv.visitInsn(Opcodes.RETURN);
762 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
763 char rtype = Wrapper.basicTypeChar(invokerType.returnType());
765 // put return value on the stack if it is not already there
766 if (lambdaForm.result != lambdaForm.names.length - 1) {
767 emitLoadInsn(rn.type, lambdaForm.result);
770 // potentially generate cast
771 // rtype is the return type of the invoker - generated code must conform to this
772 // rn.type is the type of the result Name in the LF
773 if (rtype != rn.type) {
776 // possibly cast the primitive to the correct type for boxing
777 char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
778 if (boxedType != rn.type) {
779 emitPrimCast(rn.type, boxedType);
781 // cast primitive to reference ("boxing")
782 emitBoxing(invokerType.returnType());
785 if (rn.type != 'L') {
787 emitPrimCast(rn.type, rtype);
789 // ref-to-prim cast ("unboxing")
790 throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
795 // generate actual return statement
796 emitReturnInsn(invokerType.returnType());
801 * Emit a type conversion bytecode casting from "from" to "to".
803 private void emitPrimCast(char from, char to) {
805 // - indicates forbidden
806 // <-> indicates implicit
807 // to ----> boolean byte short char int long float double
808 // from boolean <-> - - - - - - -
809 // byte - <-> i2s i2c <-> i2l i2f i2d
810 // short - i2b <-> i2c <-> i2l i2f i2d
811 // char - i2b i2s <-> <-> i2l i2f i2d
812 // int - i2b i2s i2c <-> i2l i2f i2d
813 // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
814 // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
815 // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
817 // no cast required, should be dead code anyway
820 Wrapper wfrom = Wrapper.forBasicType(from);
821 Wrapper wto = Wrapper.forBasicType(to);
822 if (wfrom.isSubwordOrInt()) {
823 // cast from {byte,short,char,int} to anything
826 // cast from {long,float,double} to anything
827 if (wto.isSubwordOrInt()) {
828 // cast to {byte,short,char,int}
830 if (wto.bitWidth() < 32) {
831 // targets other than int require another conversion
835 // cast to {long,float,double} - this is verbose
836 boolean error = false;
839 if (to == 'F') { mv.visitInsn(Opcodes.L2F); }
840 else if (to == 'D') { mv.visitInsn(Opcodes.L2D); }
844 if (to == 'J') { mv.visitInsn(Opcodes.F2L); }
845 else if (to == 'D') { mv.visitInsn(Opcodes.F2D); }
849 if (to == 'J') { mv.visitInsn(Opcodes.D2L); }
850 else if (to == 'F') { mv.visitInsn(Opcodes.D2F); }
858 throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
864 private void emitI2X(char type) {
866 case 'B': mv.visitInsn(Opcodes.I2B); break;
867 case 'S': mv.visitInsn(Opcodes.I2S); break;
868 case 'C': mv.visitInsn(Opcodes.I2C); break;
869 case 'I': /* naught */ break;
870 case 'J': mv.visitInsn(Opcodes.I2L); break;
871 case 'F': mv.visitInsn(Opcodes.I2F); break;
872 case 'D': mv.visitInsn(Opcodes.I2D); break;
874 // For compatibility with ValueConversions and explicitCastArguments:
875 mv.visitInsn(Opcodes.ICONST_1);
876 mv.visitInsn(Opcodes.IAND);
878 default: throw new InternalError("unknown type: " + type);
882 private void emitX2I(char type) {
884 case 'J': mv.visitInsn(Opcodes.L2I); break;
885 case 'F': mv.visitInsn(Opcodes.F2I); break;
886 case 'D': mv.visitInsn(Opcodes.D2I); break;
887 default: throw new InternalError("unknown type: " + type);
891 private static String basicTypeCharSignature(String prefix, MethodType type) {
892 StringBuilder buf = new StringBuilder(prefix);
893 for (Class<?> ptype : type.parameterList())
894 buf.append(Wrapper.forBasicType(ptype).basicTypeChar());
895 buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar());
896 return buf.toString();
900 * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
902 static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
903 assert(LambdaForm.isValidSignature(sig));
904 //System.out.println("generateExactInvoker "+sig);
905 // compute method type
906 // first parameter and return type
907 char tret = LambdaForm.signatureReturn(sig);
908 MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
909 // other parameter types
910 int arity = LambdaForm.signatureArity(sig);
911 for (int i = 1; i < arity; i++) {
912 type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
914 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
915 return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
918 private byte[] generateLambdaFormInterpreterEntryPointBytes() {
921 // Suppress this method in backtraces displayed to the user.
922 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
924 // Don't inline the interpreter entry.
925 mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
927 // create parameter array
928 emitIconstInsn(invokerType.parameterCount());
929 mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
931 // fill parameter array
932 for (int i = 0; i < invokerType.parameterCount(); i++) {
933 Class<?> ptype = invokerType.parameterType(i);
934 mv.visitInsn(Opcodes.DUP);
936 emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
937 // box if primitive type
938 if (ptype.isPrimitive()) {
941 mv.visitInsn(Opcodes.AASTORE);
945 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
946 mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
947 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
950 Class<?> rtype = invokerType.returnType();
951 if (rtype.isPrimitive() && rtype != void.class) {
952 emitUnboxing(Wrapper.asWrapperType(rtype));
956 emitReturnInsn(rtype);
959 bogusMethod(invokerType);
961 final byte[] classFile = cw.toByteArray();
962 maybeDump(className, classFile);
967 * Generate bytecode for a NamedFunction invoker.
969 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
970 MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE;
971 String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType());
972 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
973 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
978 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
979 MethodType dstType = typeForm.erasedType();
982 // Suppress this method in backtraces displayed to the user.
983 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
985 // Force inlining of this invoker method.
986 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
991 // Load arguments from array
992 for (int i = 0; i < dstType.parameterCount(); i++) {
995 mv.visitInsn(Opcodes.AALOAD);
998 Class<?> dptype = dstType.parameterType(i);
999 if (dptype.isPrimitive()) {
1000 Class<?> sptype = dstType.basicType().wrap().parameterType(i);
1001 Wrapper dstWrapper = Wrapper.forBasicType(dptype);
1002 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int
1003 emitUnboxing(srcWrapper.wrapperType());
1004 emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
1009 String targetDesc = dstType.basicType().toMethodDescriptorString();
1010 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc);
1012 // Box primitive types
1013 Class<?> rtype = dstType.returnType();
1014 if (rtype != void.class && rtype.isPrimitive()) {
1015 Wrapper srcWrapper = Wrapper.forBasicType(rtype);
1016 Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
1017 // boolean casts not allowed
1018 emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
1019 emitBoxing(dstWrapper.primitiveType());
1022 // If the return type is void we return a null reference.
1023 if (rtype == void.class) {
1024 mv.visitInsn(Opcodes.ACONST_NULL);
1026 emitReturnInsn(Object.class); // NOTE: NamedFunction invokers always return a reference value.
1028 classFileEpilogue();
1029 bogusMethod(dstType);
1031 final byte[] classFile = cw.toByteArray();
1032 maybeDump(className, classFile);
1037 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
1038 * for debugging purposes.
1040 private void bogusMethod(Object... os) {
1041 if (DUMP_CLASS_FILES) {
1042 mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
1043 for (Object o : os) {
1044 mv.visitLdcInsn(o.toString());
1045 mv.visitInsn(Opcodes.POP);
1047 mv.visitInsn(Opcodes.RETURN);