rev 12972 : 8140606: Update library code to use internal Unsafe
Reviewed-by: duke
1 /*
2 * Copyright (c) 2012, 2015, 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.*;
29 import java.util.*;
30 import java.lang.reflect.Modifier;
31
32 import jdk.internal.org.objectweb.asm.*;
33
34 import static java.lang.invoke.LambdaForm.*;
35 import static java.lang.invoke.LambdaForm.BasicType.*;
36 import static java.lang.invoke.MethodHandleStatics.*;
37 import static java.lang.invoke.MethodHandleNatives.Constants.*;
38
39 import sun.invoke.util.VerifyAccess;
40 import sun.invoke.util.VerifyType;
41 import sun.invoke.util.Wrapper;
42 import sun.reflect.misc.ReflectUtil;
43
44 /**
45 * Code generation backend for LambdaForm.
46 * <p>
47 * @author John Rose, JSR 292 EG
48 */
49 class InvokerBytecodeGenerator {
50 /** Define class names for convenience. */
51 private static final String MH = "java/lang/invoke/MethodHandle";
52 private static final String MHI = "java/lang/invoke/MethodHandleImpl";
53 private static final String LF = "java/lang/invoke/LambdaForm";
54 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
55 private static final String CLS = "java/lang/Class";
56 private static final String OBJ = "java/lang/Object";
57 private static final String OBJARY = "[Ljava/lang/Object;";
58
59 private static final String MH_SIG = "L" + MH + ";";
60 private static final String LF_SIG = "L" + LF + ";";
61 private static final String LFN_SIG = "L" + LFN + ";";
62 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
63 private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
64
65 /** Name of its super class*/
66 private static final String superName = OBJ;
67
68 /** Name of new class */
69 private final String className;
70
71 /** Name of the source file (for stack trace printing). */
72 private final String sourceFile;
73
74 private final LambdaForm lambdaForm;
75 private final String invokerName;
76 private final MethodType invokerType;
77
78 /** Info about local variables in compiled lambda form */
79 private final int[] localsMap; // index
80 private final BasicType[] localTypes; // basic type
81 private final Class<?>[] localClasses; // type
82
83 /** ASM bytecode generation. */
84 private ClassWriter cw;
85 private MethodVisitor mv;
86
87 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
88 private static final Class<?> HOST_CLASS = LambdaForm.class;
89
90 /** Main constructor; other constructors delegate to this one. */
91 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
92 String className, String invokerName, MethodType invokerType) {
93 if (invokerName.contains(".")) {
94 int p = invokerName.indexOf('.');
95 className = invokerName.substring(0, p);
96 invokerName = invokerName.substring(p+1);
97 }
98 if (DUMP_CLASS_FILES) {
99 className = makeDumpableClassName(className);
100 }
101 this.className = LF + "$" + className;
102 this.sourceFile = "LambdaForm$" + className;
103 this.lambdaForm = lambdaForm;
104 this.invokerName = invokerName;
105 this.invokerType = invokerType;
106 this.localsMap = new int[localsMapSize+1];
107 // last entry of localsMap is count of allocated local slots
108 this.localTypes = new BasicType[localsMapSize+1];
109 this.localClasses = new Class<?>[localsMapSize+1];
110 }
111
112 /** For generating LambdaForm interpreter entry points. */
113 private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
114 this(null, invokerType.parameterCount(),
115 className, invokerName, invokerType);
116 // Create an array to map name indexes to locals indexes.
117 localTypes[localTypes.length - 1] = V_TYPE;
118 for (int i = 0; i < localsMap.length; i++) {
119 localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i);
120 if (i < invokerType.parameterCount())
121 localTypes[i] = basicType(invokerType.parameterType(i));
122 }
123 }
124
125 /** For generating customized code for a single LambdaForm. */
126 private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
127 this(form, form.names.length,
128 className, form.debugName, invokerType);
129 // Create an array to map name indexes to locals indexes.
130 Name[] names = form.names;
131 for (int i = 0, index = 0; i < localsMap.length; i++) {
132 localsMap[i] = index;
133 if (i < names.length) {
134 BasicType type = names[i].type();
135 index += type.basicTypeSlots();
136 localTypes[i] = type;
137 }
138 }
139 }
140
141
142 /** instance counters for dumped classes */
143 private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
144 /** debugging flag for saving generated class files */
145 private static final File DUMP_CLASS_FILES_DIR;
146
147 static {
148 if (DUMP_CLASS_FILES) {
149 DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
150 try {
151 File dumpDir = new File("DUMP_CLASS_FILES");
152 if (!dumpDir.exists()) {
153 dumpDir.mkdirs();
154 }
155 DUMP_CLASS_FILES_DIR = dumpDir;
156 System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
157 } catch (Exception e) {
158 throw newInternalError(e);
159 }
160 } else {
161 DUMP_CLASS_FILES_COUNTERS = null;
162 DUMP_CLASS_FILES_DIR = null;
163 }
164 }
165
166 static void maybeDump(final String className, final byte[] classFile) {
167 if (DUMP_CLASS_FILES) {
168 java.security.AccessController.doPrivileged(
169 new java.security.PrivilegedAction<>() {
170 public Void run() {
171 try {
172 String dumpName = className;
173 //dumpName = dumpName.replace('/', '-');
174 File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
175 System.out.println("dump: " + dumpFile);
176 dumpFile.getParentFile().mkdirs();
177 FileOutputStream file = new FileOutputStream(dumpFile);
178 file.write(classFile);
179 file.close();
180 return null;
181 } catch (IOException ex) {
182 throw newInternalError(ex);
183 }
184 }
185 });
186 }
187
188 }
189
190 private static String makeDumpableClassName(String className) {
191 Integer ctr;
192 synchronized (DUMP_CLASS_FILES_COUNTERS) {
193 ctr = DUMP_CLASS_FILES_COUNTERS.get(className);
194 if (ctr == null) ctr = 0;
195 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1);
196 }
197 String sfx = ctr.toString();
198 while (sfx.length() < 3)
199 sfx = "0"+sfx;
200 className += sfx;
201 return className;
202 }
203
204 class CpPatch {
205 final int index;
206 final String placeholder;
207 final Object value;
208 CpPatch(int index, String placeholder, Object value) {
209 this.index = index;
210 this.placeholder = placeholder;
211 this.value = value;
212 }
213 public String toString() {
214 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
215 }
216 }
217
218 Map<Object, CpPatch> cpPatches = new HashMap<>();
219
220 int cph = 0; // for counting constant placeholders
221
222 String constantPlaceholder(Object arg) {
223 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
224 if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>"; // debugging aid
225 if (cpPatches.containsKey(cpPlaceholder)) {
226 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
227 }
228 // insert placeholder in CP and remember the patch
229 int index = cw.newConst((Object) cpPlaceholder); // TODO check if already in the constant pool
230 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg));
231 return cpPlaceholder;
232 }
233
234 Object[] cpPatches(byte[] classFile) {
235 int size = getConstantPoolSize(classFile);
236 Object[] res = new Object[size];
237 for (CpPatch p : cpPatches.values()) {
238 if (p.index >= size)
239 throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
240 res[p.index] = p.value;
241 }
242 return res;
243 }
244
245 private static String debugString(Object arg) {
246 if (arg instanceof MethodHandle) {
247 MethodHandle mh = (MethodHandle) arg;
248 MemberName member = mh.internalMemberName();
249 if (member != null)
250 return member.toString();
251 return mh.debugString();
252 }
253 return arg.toString();
254 }
255
256 /**
257 * Extract the number of constant pool entries from a given class file.
258 *
259 * @param classFile the bytes of the class file in question.
260 * @return the number of entries in the constant pool.
261 */
262 private static int getConstantPoolSize(byte[] classFile) {
263 // The first few bytes:
264 // u4 magic;
265 // u2 minor_version;
266 // u2 major_version;
267 // u2 constant_pool_count;
268 return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
269 }
270
271 /**
272 * Extract the MemberName of a newly-defined method.
273 */
274 private MemberName loadMethod(byte[] classFile) {
275 Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
276 return resolveInvokerMember(invokerClass, invokerName, invokerType);
277 }
278
279 /**
280 * Define a given class as anonymous class in the runtime system.
281 */
282 private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
283 Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
284 UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
285 return invokerClass;
286 }
287
288 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
289 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
290 //System.out.println("resolveInvokerMember => "+member);
291 //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m);
292 try {
293 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
294 } catch (ReflectiveOperationException e) {
295 throw newInternalError(e);
296 }
297 //System.out.println("resolveInvokerMember => "+member);
298 return member;
299 }
300
301 /**
302 * Set up class file generation.
303 */
304 private void classFilePrologue() {
305 final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
306 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
307 cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
308 cw.visitSource(sourceFile, null);
309
310 String invokerDesc = invokerType.toMethodDescriptorString();
311 mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null);
312 }
313
314 /**
315 * Tear down class file generation.
316 */
317 private void classFileEpilogue() {
318 mv.visitMaxs(0, 0);
319 mv.visitEnd();
320 }
321
322 /*
323 * Low-level emit helpers.
324 */
325 private void emitConst(Object con) {
326 if (con == null) {
327 mv.visitInsn(Opcodes.ACONST_NULL);
328 return;
329 }
330 if (con instanceof Integer) {
331 emitIconstInsn((int) con);
332 return;
333 }
334 if (con instanceof Long) {
335 long x = (long) con;
336 if (x == (short) x) {
337 emitIconstInsn((int) x);
338 mv.visitInsn(Opcodes.I2L);
339 return;
340 }
341 }
342 if (con instanceof Float) {
343 float x = (float) con;
344 if (x == (short) x) {
345 emitIconstInsn((int) x);
346 mv.visitInsn(Opcodes.I2F);
347 return;
348 }
349 }
350 if (con instanceof Double) {
351 double x = (double) con;
352 if (x == (short) x) {
353 emitIconstInsn((int) x);
354 mv.visitInsn(Opcodes.I2D);
355 return;
356 }
357 }
358 if (con instanceof Boolean) {
359 emitIconstInsn((boolean) con ? 1 : 0);
360 return;
361 }
362 // fall through:
363 mv.visitLdcInsn(con);
364 }
365
366 private void emitIconstInsn(int i) {
367 int opcode;
368 switch (i) {
369 case 0: opcode = Opcodes.ICONST_0; break;
370 case 1: opcode = Opcodes.ICONST_1; break;
371 case 2: opcode = Opcodes.ICONST_2; break;
372 case 3: opcode = Opcodes.ICONST_3; break;
373 case 4: opcode = Opcodes.ICONST_4; break;
374 case 5: opcode = Opcodes.ICONST_5; break;
375 default:
376 if (i == (byte) i) {
377 mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF);
378 } else if (i == (short) i) {
379 mv.visitIntInsn(Opcodes.SIPUSH, (char) i);
380 } else {
381 mv.visitLdcInsn(i);
382 }
383 return;
384 }
385 mv.visitInsn(opcode);
386 }
387
388 /*
389 * NOTE: These load/store methods use the localsMap to find the correct index!
390 */
391 private void emitLoadInsn(BasicType type, int index) {
392 int opcode = loadInsnOpcode(type);
393 mv.visitVarInsn(opcode, localsMap[index]);
394 }
395
396 private int loadInsnOpcode(BasicType type) throws InternalError {
397 switch (type) {
398 case I_TYPE: return Opcodes.ILOAD;
399 case J_TYPE: return Opcodes.LLOAD;
400 case F_TYPE: return Opcodes.FLOAD;
401 case D_TYPE: return Opcodes.DLOAD;
402 case L_TYPE: return Opcodes.ALOAD;
403 default:
404 throw new InternalError("unknown type: " + type);
405 }
406 }
407 private void emitAloadInsn(int index) {
408 emitLoadInsn(L_TYPE, index);
409 }
410
411 private void emitStoreInsn(BasicType type, int index) {
412 int opcode = storeInsnOpcode(type);
413 mv.visitVarInsn(opcode, localsMap[index]);
414 }
415
416 private int storeInsnOpcode(BasicType type) throws InternalError {
417 switch (type) {
418 case I_TYPE: return Opcodes.ISTORE;
419 case J_TYPE: return Opcodes.LSTORE;
420 case F_TYPE: return Opcodes.FSTORE;
421 case D_TYPE: return Opcodes.DSTORE;
422 case L_TYPE: return Opcodes.ASTORE;
423 default:
424 throw new InternalError("unknown type: " + type);
425 }
426 }
427 private void emitAstoreInsn(int index) {
428 emitStoreInsn(L_TYPE, index);
429 }
430
431 private byte arrayTypeCode(Wrapper elementType) {
432 switch (elementType) {
433 case BOOLEAN: return Opcodes.T_BOOLEAN;
434 case BYTE: return Opcodes.T_BYTE;
435 case CHAR: return Opcodes.T_CHAR;
436 case SHORT: return Opcodes.T_SHORT;
437 case INT: return Opcodes.T_INT;
438 case LONG: return Opcodes.T_LONG;
439 case FLOAT: return Opcodes.T_FLOAT;
440 case DOUBLE: return Opcodes.T_DOUBLE;
441 case OBJECT: return 0; // in place of Opcodes.T_OBJECT
442 default: throw new InternalError();
443 }
444 }
445
446 private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError {
447 assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD);
448 int xas;
449 switch (tcode) {
450 case Opcodes.T_BOOLEAN: xas = Opcodes.BASTORE; break;
451 case Opcodes.T_BYTE: xas = Opcodes.BASTORE; break;
452 case Opcodes.T_CHAR: xas = Opcodes.CASTORE; break;
453 case Opcodes.T_SHORT: xas = Opcodes.SASTORE; break;
454 case Opcodes.T_INT: xas = Opcodes.IASTORE; break;
455 case Opcodes.T_LONG: xas = Opcodes.LASTORE; break;
456 case Opcodes.T_FLOAT: xas = Opcodes.FASTORE; break;
457 case Opcodes.T_DOUBLE: xas = Opcodes.DASTORE; break;
458 case 0: xas = Opcodes.AASTORE; break;
459 default: throw new InternalError();
460 }
461 return xas - Opcodes.AASTORE + aaop;
462 }
463
464
465 private void freeFrameLocal(int oldFrameLocal) {
466 int i = indexForFrameLocal(oldFrameLocal);
467 if (i < 0) return;
468 BasicType type = localTypes[i];
469 int newFrameLocal = makeLocalTemp(type);
470 mv.visitVarInsn(loadInsnOpcode(type), oldFrameLocal);
471 mv.visitVarInsn(storeInsnOpcode(type), newFrameLocal);
472 assert(localsMap[i] == oldFrameLocal);
473 localsMap[i] = newFrameLocal;
474 assert(indexForFrameLocal(oldFrameLocal) < 0);
475 }
476 private int indexForFrameLocal(int frameLocal) {
477 for (int i = 0; i < localsMap.length; i++) {
478 if (localsMap[i] == frameLocal && localTypes[i] != V_TYPE)
479 return i;
480 }
481 return -1;
482 }
483 private int makeLocalTemp(BasicType type) {
484 int frameLocal = localsMap[localsMap.length - 1];
485 localsMap[localsMap.length - 1] = frameLocal + type.basicTypeSlots();
486 return frameLocal;
487 }
488
489 /**
490 * Emit a boxing call.
491 *
492 * @param wrapper primitive type class to box.
493 */
494 private void emitBoxing(Wrapper wrapper) {
495 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
496 String name = "valueOf";
497 String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
498 mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false);
499 }
500
501 /**
502 * Emit an unboxing call (plus preceding checkcast).
503 *
504 * @param wrapper wrapper type class to unbox.
505 */
506 private void emitUnboxing(Wrapper wrapper) {
507 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
508 String name = wrapper.primitiveSimpleName() + "Value";
509 String desc = "()" + wrapper.basicTypeChar();
510 emitReferenceCast(wrapper.wrapperType(), null);
511 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false);
512 }
513
514 /**
515 * Emit an implicit conversion for an argument which must be of the given pclass.
516 * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
517 *
518 * @param ptype type of value present on stack
519 * @param pclass type of value required on stack
520 * @param arg compile-time representation of value on stack (Node, constant) or null if none
521 */
522 private void emitImplicitConversion(BasicType ptype, Class<?> pclass, Object arg) {
523 assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller
524 if (pclass == ptype.basicTypeClass() && ptype != L_TYPE)
525 return; // nothing to do
526 switch (ptype) {
527 case L_TYPE:
528 if (VerifyType.isNullConversion(Object.class, pclass, false)) {
529 if (PROFILE_LEVEL > 0)
530 emitReferenceCast(Object.class, arg);
531 return;
532 }
533 emitReferenceCast(pclass, arg);
534 return;
535 case I_TYPE:
536 if (!VerifyType.isNullConversion(int.class, pclass, false))
537 emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
538 return;
539 }
540 throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass);
541 }
542
543 /** Update localClasses type map. Return true if the information is already present. */
544 private boolean assertStaticType(Class<?> cls, Name n) {
545 int local = n.index();
546 Class<?> aclass = localClasses[local];
547 if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) {
548 return true; // type info is already present
549 } else if (aclass == null || aclass.isAssignableFrom(cls)) {
550 localClasses[local] = cls; // type info can be improved
551 }
552 return false;
553 }
554
555 private void emitReferenceCast(Class<?> cls, Object arg) {
556 Name writeBack = null; // local to write back result
557 if (arg instanceof Name) {
558 Name n = (Name) arg;
559 if (assertStaticType(cls, n))
560 return; // this cast was already performed
561 if (lambdaForm.useCount(n) > 1) {
562 // This guy gets used more than once.
563 writeBack = n;
564 }
565 }
566 if (isStaticallyNameable(cls)) {
567 String sig = getInternalName(cls);
568 mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
569 } else {
570 mv.visitLdcInsn(constantPlaceholder(cls));
571 mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
572 mv.visitInsn(Opcodes.SWAP);
573 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
574 if (Object[].class.isAssignableFrom(cls))
575 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
576 else if (PROFILE_LEVEL > 0)
577 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ);
578 }
579 if (writeBack != null) {
580 mv.visitInsn(Opcodes.DUP);
581 emitAstoreInsn(writeBack.index());
582 }
583 }
584
585 /**
586 * Emits an actual return instruction conforming to the given return type.
587 */
588 private void emitReturnInsn(BasicType type) {
589 int opcode;
590 switch (type) {
591 case I_TYPE: opcode = Opcodes.IRETURN; break;
592 case J_TYPE: opcode = Opcodes.LRETURN; break;
593 case F_TYPE: opcode = Opcodes.FRETURN; break;
594 case D_TYPE: opcode = Opcodes.DRETURN; break;
595 case L_TYPE: opcode = Opcodes.ARETURN; break;
596 case V_TYPE: opcode = Opcodes.RETURN; break;
597 default:
598 throw new InternalError("unknown return type: " + type);
599 }
600 mv.visitInsn(opcode);
601 }
602
603 private static String getInternalName(Class<?> c) {
604 if (c == Object.class) return OBJ;
605 else if (c == Object[].class) return OBJARY;
606 else if (c == Class.class) return CLS;
607 else if (c == MethodHandle.class) return MH;
608 assert(VerifyAccess.isTypeVisible(c, Object.class)) : c.getName();
609 return c.getName().replace('.', '/');
610 }
611
612 /**
613 * Generate customized bytecode for a given LambdaForm.
614 */
615 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
616 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
617 return g.loadMethod(g.generateCustomizedCodeBytes());
618 }
619
620 /** Generates code to check that actual receiver and LambdaForm matches */
621 private boolean checkActualReceiver() {
622 // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0
623 mv.visitInsn(Opcodes.DUP);
624 mv.visitVarInsn(Opcodes.ALOAD, localsMap[0]);
625 mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "assertSame", LLV_SIG, false);
626 return true;
627 }
628
629 /**
630 * Generate an invoker method for the passed {@link LambdaForm}.
631 */
632 private byte[] generateCustomizedCodeBytes() {
633 classFilePrologue();
634
635 // Suppress this method in backtraces displayed to the user.
636 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
637
638 // Mark this method as a compiled LambdaForm
639 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
640
641 if (lambdaForm.forceInline) {
642 // Force inlining of this invoker method.
643 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
644 } else {
645 mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
646 }
647
648 if (lambdaForm.customized != null) {
649 // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
650 // receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
651 // It enables more efficient code generation in some situations, since embedded constants
652 // are compile-time constants for JIT compiler.
653 mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
654 mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
655 assert(checkActualReceiver()); // expects MethodHandle on top of the stack
656 mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
657 }
658
659 // iterate over the form's names, generating bytecode instructions for each
660 // start iterating at the first name following the arguments
661 Name onStack = null;
662 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
663 Name name = lambdaForm.names[i];
664
665 emitStoreResult(onStack);
666 onStack = name; // unless otherwise modified below
667 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
668 switch (intr) {
669 case SELECT_ALTERNATIVE:
670 assert isSelectAlternative(i);
671 if (PROFILE_GWT) {
672 assert(name.arguments[0] instanceof Name &&
673 nameRefersTo((Name)name.arguments[0], MethodHandleImpl.class, "profileBoolean"));
674 mv.visitAnnotation("Ljava/lang/invoke/InjectedProfile;", true);
675 }
676 onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
677 i++; // skip MH.invokeBasic of the selectAlternative result
678 continue;
679 case GUARD_WITH_CATCH:
680 assert isGuardWithCatch(i);
681 onStack = emitGuardWithCatch(i);
682 i = i+2; // Jump to the end of GWC idiom
683 continue;
684 case NEW_ARRAY:
685 Class<?> rtype = name.function.methodType().returnType();
686 if (isStaticallyNameable(rtype)) {
687 emitNewArray(name);
688 continue;
689 }
690 break;
691 case ARRAY_LOAD:
692 emitArrayLoad(name);
693 continue;
694 case ARRAY_STORE:
695 emitArrayStore(name);
696 continue;
697 case IDENTITY:
698 assert(name.arguments.length == 1);
699 emitPushArguments(name);
700 continue;
701 case ZERO:
702 assert(name.arguments.length == 0);
703 emitConst(name.type.basicTypeWrapper().zero());
704 continue;
705 case NONE:
706 // no intrinsic associated
707 break;
708 default:
709 throw newInternalError("Unknown intrinsic: "+intr);
710 }
711
712 MemberName member = name.function.member();
713 if (isStaticallyInvocable(member)) {
714 emitStaticInvoke(member, name);
715 } else {
716 emitInvoke(name);
717 }
718 }
719
720 // return statement
721 emitReturn(onStack);
722
723 classFileEpilogue();
724 bogusMethod(lambdaForm);
725
726 final byte[] classFile = cw.toByteArray();
727 maybeDump(className, classFile);
728 return classFile;
729 }
730
731 void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); }
732 void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
733
734 void emitArrayOp(Name name, int arrayOpcode) {
735 assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE;
736 Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
737 assert elementType != null;
738 emitPushArguments(name);
739 if (elementType.isPrimitive()) {
740 Wrapper w = Wrapper.forPrimitiveType(elementType);
741 arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
742 }
743 mv.visitInsn(arrayOpcode);
744 }
745
746 /**
747 * Emit an invoke for the given name.
748 */
749 void emitInvoke(Name name) {
750 assert(!isLinkerMethodInvoke(name)); // should use the static path for these
751 if (true) {
752 // push receiver
753 MethodHandle target = name.function.resolvedHandle;
754 assert(target != null) : name.exprString();
755 mv.visitLdcInsn(constantPlaceholder(target));
756 emitReferenceCast(MethodHandle.class, target);
757 } else {
758 // load receiver
759 emitAloadInsn(0);
760 emitReferenceCast(MethodHandle.class, null);
761 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
762 mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
763 // TODO more to come
764 }
765
766 // push arguments
767 emitPushArguments(name);
768
769 // invocation
770 MethodType type = name.function.methodType();
771 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
772 }
773
774 private static Class<?>[] STATICALLY_INVOCABLE_PACKAGES = {
775 // Sample classes from each package we are willing to bind to statically:
776 java.lang.Object.class,
777 java.util.Arrays.class,
778 sun.misc.Unsafe.class
779 //MethodHandle.class already covered
780 };
781
782 static boolean isStaticallyInvocable(Name name) {
783 return isStaticallyInvocable(name.function.member());
784 }
785
786 static boolean isStaticallyInvocable(MemberName member) {
787 if (member == null) return false;
788 if (member.isConstructor()) return false;
789 Class<?> cls = member.getDeclaringClass();
790 if (cls.isArray() || cls.isPrimitive())
791 return false; // FIXME
792 if (cls.isAnonymousClass() || cls.isLocalClass())
793 return false; // inner class of some sort
794 if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
795 return false; // not on BCP
796 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
797 return false;
798 MethodType mtype = member.getMethodOrFieldType();
799 if (!isStaticallyNameable(mtype.returnType()))
800 return false;
801 for (Class<?> ptype : mtype.parameterArray())
802 if (!isStaticallyNameable(ptype))
803 return false;
804 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
805 return true; // in java.lang.invoke package
806 if (member.isPublic() && isStaticallyNameable(cls))
807 return true;
808 return false;
809 }
810
811 static boolean isStaticallyNameable(Class<?> cls) {
812 if (cls == Object.class)
813 return true;
814 while (cls.isArray())
815 cls = cls.getComponentType();
816 if (cls.isPrimitive())
817 return true; // int[].class, for example
818 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
819 return false;
820 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
821 if (cls.getClassLoader() != Object.class.getClassLoader())
822 return false;
823 if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
824 return true;
825 if (!Modifier.isPublic(cls.getModifiers()))
826 return false;
827 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
828 if (VerifyAccess.isSamePackage(pkgcls, cls))
829 return true;
830 }
831 return false;
832 }
833
834 void emitStaticInvoke(Name name) {
835 emitStaticInvoke(name.function.member(), name);
836 }
837
838 /**
839 * Emit an invoke for the given name, using the MemberName directly.
840 */
841 void emitStaticInvoke(MemberName member, Name name) {
842 assert(member.equals(name.function.member()));
843 Class<?> defc = member.getDeclaringClass();
844 String cname = getInternalName(defc);
845 String mname = member.getName();
846 String mtype;
847 byte refKind = member.getReferenceKind();
848 if (refKind == REF_invokeSpecial) {
849 // in order to pass the verifier, we need to convert this to invokevirtual in all cases
850 assert(member.canBeStaticallyBound()) : member;
851 refKind = REF_invokeVirtual;
852 }
853
854 assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
855
856 // push arguments
857 emitPushArguments(name);
858
859 // invocation
860 if (member.isMethod()) {
861 mtype = member.getMethodType().toMethodDescriptorString();
862 mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype,
863 member.getDeclaringClass().isInterface());
864 } else {
865 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
866 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
867 }
868 // Issue a type assertion for the result, so we can avoid casts later.
869 if (name.type == L_TYPE) {
870 Class<?> rtype = member.getInvocationType().returnType();
871 assert(!rtype.isPrimitive());
872 if (rtype != Object.class && !rtype.isInterface()) {
873 assertStaticType(rtype, name);
874 }
875 }
876 }
877
878 void emitNewArray(Name name) throws InternalError {
879 Class<?> rtype = name.function.methodType().returnType();
880 if (name.arguments.length == 0) {
881 // The array will be a constant.
882 Object emptyArray;
883 try {
884 emptyArray = name.function.resolvedHandle.invoke();
885 } catch (Throwable ex) {
886 throw newInternalError(ex);
887 }
888 assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
889 assert(emptyArray.getClass() == rtype); // exact typing
890 mv.visitLdcInsn(constantPlaceholder(emptyArray));
891 emitReferenceCast(rtype, emptyArray);
892 return;
893 }
894 Class<?> arrayElementType = rtype.getComponentType();
895 assert(arrayElementType != null);
896 emitIconstInsn(name.arguments.length);
897 int xas = Opcodes.AASTORE;
898 if (!arrayElementType.isPrimitive()) {
899 mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
900 } else {
901 byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
902 xas = arrayInsnOpcode(tc, xas);
903 mv.visitIntInsn(Opcodes.NEWARRAY, tc);
904 }
905 // store arguments
906 for (int i = 0; i < name.arguments.length; i++) {
907 mv.visitInsn(Opcodes.DUP);
908 emitIconstInsn(i);
909 emitPushArgument(name, i);
910 mv.visitInsn(xas);
911 }
912 // the array is left on the stack
913 assertStaticType(rtype, name);
914 }
915 int refKindOpcode(byte refKind) {
916 switch (refKind) {
917 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
918 case REF_invokeStatic: return Opcodes.INVOKESTATIC;
919 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
920 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
921 case REF_getField: return Opcodes.GETFIELD;
922 case REF_putField: return Opcodes.PUTFIELD;
923 case REF_getStatic: return Opcodes.GETSTATIC;
924 case REF_putStatic: return Opcodes.PUTSTATIC;
925 }
926 throw new InternalError("refKind="+refKind);
927 }
928
929 /**
930 * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
931 */
932 private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) {
933 return member != null &&
934 member.getDeclaringClass() == declaringClass &&
935 member.getName().equals(name);
936 }
937 private boolean nameRefersTo(Name name, Class<?> declaringClass, String methodName) {
938 return name.function != null &&
939 memberRefersTo(name.function.member(), declaringClass, methodName);
940 }
941
942 /**
943 * Check if MemberName is a call to MethodHandle.invokeBasic.
944 */
945 private boolean isInvokeBasic(Name name) {
946 if (name.function == null)
947 return false;
948 if (name.arguments.length < 1)
949 return false; // must have MH argument
950 MemberName member = name.function.member();
951 return memberRefersTo(member, MethodHandle.class, "invokeBasic") &&
952 !member.isPublic() && !member.isStatic();
953 }
954
955 /**
956 * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
957 */
958 private boolean isLinkerMethodInvoke(Name name) {
959 if (name.function == null)
960 return false;
961 if (name.arguments.length < 1)
962 return false; // must have MH argument
963 MemberName member = name.function.member();
964 return member != null &&
965 member.getDeclaringClass() == MethodHandle.class &&
966 !member.isPublic() && member.isStatic() &&
967 member.getName().startsWith("linkTo");
968 }
969
970 /**
971 * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
972 */
973 private boolean isSelectAlternative(int pos) {
974 // selectAlternative idiom:
975 // t_{n}:L=MethodHandleImpl.selectAlternative(...)
976 // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
977 if (pos+1 >= lambdaForm.names.length) return false;
978 Name name0 = lambdaForm.names[pos];
979 Name name1 = lambdaForm.names[pos+1];
980 return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") &&
981 isInvokeBasic(name1) &&
982 name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
983 lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1}
984 }
985
986 /**
987 * Check if i-th name is a start of GuardWithCatch idiom.
988 */
989 private boolean isGuardWithCatch(int pos) {
990 // GuardWithCatch idiom:
991 // t_{n}:L=MethodHandle.invokeBasic(...)
992 // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
993 // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
994 if (pos+2 >= lambdaForm.names.length) return false;
995 Name name0 = lambdaForm.names[pos];
996 Name name1 = lambdaForm.names[pos+1];
997 Name name2 = lambdaForm.names[pos+2];
998 return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") &&
999 isInvokeBasic(name0) &&
1000 isInvokeBasic(name2) &&
1001 name1.lastUseIndex(name0) == 3 && // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
1002 lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1}
1003 name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
1004 lambdaForm.lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2}
1005 }
1006
1007 /**
1008 * Emit bytecode for the selectAlternative idiom.
1009 *
1010 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
1011 * <blockquote><pre>{@code
1012 * Lambda(a0:L,a1:I)=>{
1013 * t2:I=foo.test(a1:I);
1014 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
1015 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
1016 * }</pre></blockquote>
1017 */
1018 private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
1019 assert isStaticallyInvocable(invokeBasicName);
1020
1021 Name receiver = (Name) invokeBasicName.arguments[0];
1022
1023 Label L_fallback = new Label();
1024 Label L_done = new Label();
1025
1026 // load test result
1027 emitPushArgument(selectAlternativeName, 0);
1028
1029 // if_icmpne L_fallback
1030 mv.visitJumpInsn(Opcodes.IFEQ, L_fallback);
1031
1032 // invoke selectAlternativeName.arguments[1]
1033 Class<?>[] preForkClasses = localClasses.clone();
1034 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
1035 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
1036 emitStaticInvoke(invokeBasicName);
1037
1038 // goto L_done
1039 mv.visitJumpInsn(Opcodes.GOTO, L_done);
1040
1041 // L_fallback:
1042 mv.visitLabel(L_fallback);
1043
1044 // invoke selectAlternativeName.arguments[2]
1045 System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
1046 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
1047 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
1048 emitStaticInvoke(invokeBasicName);
1049
1050 // L_done:
1051 mv.visitLabel(L_done);
1052 // for now do not bother to merge typestate; just reset to the dominator state
1053 System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
1054
1055 return invokeBasicName; // return what's on stack
1056 }
1057
1058 /**
1059 * Emit bytecode for the guardWithCatch idiom.
1060 *
1061 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch):
1062 * <blockquote><pre>{@code
1063 * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
1064 * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
1065 * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
1066 * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
1067 * }</pre></blockquote>
1068 *
1069 * It is compiled into bytecode equivalent of the following code:
1070 * <blockquote><pre>{@code
1071 * try {
1072 * return a1.invokeBasic(a6, a7);
1073 * } catch (Throwable e) {
1074 * if (!a2.isInstance(e)) throw e;
1075 * return a3.invokeBasic(ex, a6, a7);
1076 * }}
1077 */
1078 private Name emitGuardWithCatch(int pos) {
1079 Name args = lambdaForm.names[pos];
1080 Name invoker = lambdaForm.names[pos+1];
1081 Name result = lambdaForm.names[pos+2];
1082
1083 Label L_startBlock = new Label();
1084 Label L_endBlock = new Label();
1085 Label L_handler = new Label();
1086 Label L_done = new Label();
1087
1088 Class<?> returnType = result.function.resolvedHandle.type().returnType();
1089 MethodType type = args.function.resolvedHandle.type()
1090 .dropParameterTypes(0,1)
1091 .changeReturnType(returnType);
1092
1093 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
1094
1095 // Normal case
1096 mv.visitLabel(L_startBlock);
1097 // load target
1098 emitPushArgument(invoker, 0);
1099 emitPushArguments(args, 1); // skip 1st argument: method handle
1100 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
1101 mv.visitLabel(L_endBlock);
1102 mv.visitJumpInsn(Opcodes.GOTO, L_done);
1103
1104 // Exceptional case
1105 mv.visitLabel(L_handler);
1106
1107 // Check exception's type
1108 mv.visitInsn(Opcodes.DUP);
1109 // load exception class
1110 emitPushArgument(invoker, 1);
1111 mv.visitInsn(Opcodes.SWAP);
1112 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z", false);
1113 Label L_rethrow = new Label();
1114 mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
1115
1116 // Invoke catcher
1117 // load catcher
1118 emitPushArgument(invoker, 2);
1119 mv.visitInsn(Opcodes.SWAP);
1120 emitPushArguments(args, 1); // skip 1st argument: method handle
1121 MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
1122 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false);
1123 mv.visitJumpInsn(Opcodes.GOTO, L_done);
1124
1125 mv.visitLabel(L_rethrow);
1126 mv.visitInsn(Opcodes.ATHROW);
1127
1128 mv.visitLabel(L_done);
1129
1130 return result;
1131 }
1132
1133 private void emitPushArguments(Name args) {
1134 emitPushArguments(args, 0);
1135 }
1136
1137 private void emitPushArguments(Name args, int start) {
1138 for (int i = start; i < args.arguments.length; i++) {
1139 emitPushArgument(args, i);
1140 }
1141 }
1142
1143 private void emitPushArgument(Name name, int paramIndex) {
1144 Object arg = name.arguments[paramIndex];
1145 Class<?> ptype = name.function.methodType().parameterType(paramIndex);
1146 emitPushArgument(ptype, arg);
1147 }
1148
1149 private void emitPushArgument(Class<?> ptype, Object arg) {
1150 BasicType bptype = basicType(ptype);
1151 if (arg instanceof Name) {
1152 Name n = (Name) arg;
1153 emitLoadInsn(n.type, n.index());
1154 emitImplicitConversion(n.type, ptype, n);
1155 } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
1156 emitConst(arg);
1157 } else {
1158 if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
1159 emitConst(arg);
1160 } else {
1161 mv.visitLdcInsn(constantPlaceholder(arg));
1162 emitImplicitConversion(L_TYPE, ptype, arg);
1163 }
1164 }
1165 }
1166
1167 /**
1168 * Store the name to its local, if necessary.
1169 */
1170 private void emitStoreResult(Name name) {
1171 if (name != null && name.type != V_TYPE) {
1172 // non-void: actually assign
1173 emitStoreInsn(name.type, name.index());
1174 }
1175 }
1176
1177 /**
1178 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
1179 */
1180 private void emitReturn(Name onStack) {
1181 // return statement
1182 Class<?> rclass = invokerType.returnType();
1183 BasicType rtype = lambdaForm.returnType();
1184 assert(rtype == basicType(rclass)); // must agree
1185 if (rtype == V_TYPE) {
1186 // void
1187 mv.visitInsn(Opcodes.RETURN);
1188 // it doesn't matter what rclass is; the JVM will discard any value
1189 } else {
1190 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
1191
1192 // put return value on the stack if it is not already there
1193 if (rn != onStack) {
1194 emitLoadInsn(rtype, lambdaForm.result);
1195 }
1196
1197 emitImplicitConversion(rtype, rclass, rn);
1198
1199 // generate actual return statement
1200 emitReturnInsn(rtype);
1201 }
1202 }
1203
1204 /**
1205 * Emit a type conversion bytecode casting from "from" to "to".
1206 */
1207 private void emitPrimCast(Wrapper from, Wrapper to) {
1208 // Here's how.
1209 // - indicates forbidden
1210 // <-> indicates implicit
1211 // to ----> boolean byte short char int long float double
1212 // from boolean <-> - - - - - - -
1213 // byte - <-> i2s i2c <-> i2l i2f i2d
1214 // short - i2b <-> i2c <-> i2l i2f i2d
1215 // char - i2b i2s <-> <-> i2l i2f i2d
1216 // int - i2b i2s i2c <-> i2l i2f i2d
1217 // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
1218 // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
1219 // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
1220 if (from == to) {
1221 // no cast required, should be dead code anyway
1222 return;
1223 }
1224 if (from.isSubwordOrInt()) {
1225 // cast from {byte,short,char,int} to anything
1226 emitI2X(to);
1227 } else {
1228 // cast from {long,float,double} to anything
1229 if (to.isSubwordOrInt()) {
1230 // cast to {byte,short,char,int}
1231 emitX2I(from);
1232 if (to.bitWidth() < 32) {
1233 // targets other than int require another conversion
1234 emitI2X(to);
1235 }
1236 } else {
1237 // cast to {long,float,double} - this is verbose
1238 boolean error = false;
1239 switch (from) {
1240 case LONG:
1241 switch (to) {
1242 case FLOAT: mv.visitInsn(Opcodes.L2F); break;
1243 case DOUBLE: mv.visitInsn(Opcodes.L2D); break;
1244 default: error = true; break;
1245 }
1246 break;
1247 case FLOAT:
1248 switch (to) {
1249 case LONG : mv.visitInsn(Opcodes.F2L); break;
1250 case DOUBLE: mv.visitInsn(Opcodes.F2D); break;
1251 default: error = true; break;
1252 }
1253 break;
1254 case DOUBLE:
1255 switch (to) {
1256 case LONG : mv.visitInsn(Opcodes.D2L); break;
1257 case FLOAT: mv.visitInsn(Opcodes.D2F); break;
1258 default: error = true; break;
1259 }
1260 break;
1261 default:
1262 error = true;
1263 break;
1264 }
1265 if (error) {
1266 throw new IllegalStateException("unhandled prim cast: " + from + "2" + to);
1267 }
1268 }
1269 }
1270 }
1271
1272 private void emitI2X(Wrapper type) {
1273 switch (type) {
1274 case BYTE: mv.visitInsn(Opcodes.I2B); break;
1275 case SHORT: mv.visitInsn(Opcodes.I2S); break;
1276 case CHAR: mv.visitInsn(Opcodes.I2C); break;
1277 case INT: /* naught */ break;
1278 case LONG: mv.visitInsn(Opcodes.I2L); break;
1279 case FLOAT: mv.visitInsn(Opcodes.I2F); break;
1280 case DOUBLE: mv.visitInsn(Opcodes.I2D); break;
1281 case BOOLEAN:
1282 // For compatibility with ValueConversions and explicitCastArguments:
1283 mv.visitInsn(Opcodes.ICONST_1);
1284 mv.visitInsn(Opcodes.IAND);
1285 break;
1286 default: throw new InternalError("unknown type: " + type);
1287 }
1288 }
1289
1290 private void emitX2I(Wrapper type) {
1291 switch (type) {
1292 case LONG: mv.visitInsn(Opcodes.L2I); break;
1293 case FLOAT: mv.visitInsn(Opcodes.F2I); break;
1294 case DOUBLE: mv.visitInsn(Opcodes.D2I); break;
1295 default: throw new InternalError("unknown type: " + type);
1296 }
1297 }
1298
1299 /**
1300 * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments.
1301 */
1302 static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
1303 assert(isValidSignature(sig));
1304 String name = "interpret_"+signatureReturn(sig).basicTypeChar();
1305 MethodType type = signatureType(sig); // sig includes leading argument
1306 type = type.changeParameterType(0, MethodHandle.class);
1307 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", name, type);
1308 return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
1309 }
1310
1311 private byte[] generateLambdaFormInterpreterEntryPointBytes() {
1312 classFilePrologue();
1313
1314 // Suppress this method in backtraces displayed to the user.
1315 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
1316
1317 // Don't inline the interpreter entry.
1318 mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
1319
1320 // create parameter array
1321 emitIconstInsn(invokerType.parameterCount());
1322 mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
1323
1324 // fill parameter array
1325 for (int i = 0; i < invokerType.parameterCount(); i++) {
1326 Class<?> ptype = invokerType.parameterType(i);
1327 mv.visitInsn(Opcodes.DUP);
1328 emitIconstInsn(i);
1329 emitLoadInsn(basicType(ptype), i);
1330 // box if primitive type
1331 if (ptype.isPrimitive()) {
1332 emitBoxing(Wrapper.forPrimitiveType(ptype));
1333 }
1334 mv.visitInsn(Opcodes.AASTORE);
1335 }
1336 // invoke
1337 emitAloadInsn(0);
1338 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
1339 mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable
1340 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;", false);
1341
1342 // maybe unbox
1343 Class<?> rtype = invokerType.returnType();
1344 if (rtype.isPrimitive() && rtype != void.class) {
1345 emitUnboxing(Wrapper.forPrimitiveType(rtype));
1346 }
1347
1348 // return statement
1349 emitReturnInsn(basicType(rtype));
1350
1351 classFileEpilogue();
1352 bogusMethod(invokerType);
1353
1354 final byte[] classFile = cw.toByteArray();
1355 maybeDump(className, classFile);
1356 return classFile;
1357 }
1358
1359 /**
1360 * Generate bytecode for a NamedFunction invoker.
1361 */
1362 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) {
1363 MethodType invokerType = NamedFunction.INVOKER_METHOD_TYPE;
1364 String invokerName = "invoke_" + shortenSignature(basicTypeSignature(typeForm.erasedType()));
1365 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType);
1366 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm));
1367 }
1368
1369 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
1370 MethodType dstType = typeForm.erasedType();
1371 classFilePrologue();
1372
1373 // Suppress this method in backtraces displayed to the user.
1374 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
1375
1376 // Force inlining of this invoker method.
1377 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
1378
1379 // Load receiver
1380 emitAloadInsn(0);
1381
1382 // Load arguments from array
1383 for (int i = 0; i < dstType.parameterCount(); i++) {
1384 emitAloadInsn(1);
1385 emitIconstInsn(i);
1386 mv.visitInsn(Opcodes.AALOAD);
1387
1388 // Maybe unbox
1389 Class<?> dptype = dstType.parameterType(i);
1390 if (dptype.isPrimitive()) {
1391 Class<?> sptype = dstType.basicType().wrap().parameterType(i);
1392 Wrapper dstWrapper = Wrapper.forBasicType(dptype);
1393 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int
1394 emitUnboxing(srcWrapper);
1395 emitPrimCast(srcWrapper, dstWrapper);
1396 }
1397 }
1398
1399 // Invoke
1400 String targetDesc = dstType.basicType().toMethodDescriptorString();
1401 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc, false);
1402
1403 // Box primitive types
1404 Class<?> rtype = dstType.returnType();
1405 if (rtype != void.class && rtype.isPrimitive()) {
1406 Wrapper srcWrapper = Wrapper.forBasicType(rtype);
1407 Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int
1408 // boolean casts not allowed
1409 emitPrimCast(srcWrapper, dstWrapper);
1410 emitBoxing(dstWrapper);
1411 }
1412
1413 // If the return type is void we return a null reference.
1414 if (rtype == void.class) {
1415 mv.visitInsn(Opcodes.ACONST_NULL);
1416 }
1417 emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
1418
1419 classFileEpilogue();
1420 bogusMethod(dstType);
1421
1422 final byte[] classFile = cw.toByteArray();
1423 maybeDump(className, classFile);
1424 return classFile;
1425 }
1426
1427 /**
1428 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
1429 * for debugging purposes.
1430 */
1431 private void bogusMethod(Object... os) {
1432 if (DUMP_CLASS_FILES) {
1433 mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
1434 for (Object o : os) {
1435 mv.visitLdcInsn(o.toString());
1436 mv.visitInsn(Opcodes.POP);
1437 }
1438 mv.visitInsn(Opcodes.RETURN);
1439 mv.visitMaxs(0, 0);
1440 mv.visitEnd();
1441 }
1442 }
1443 }
--- EOF ---