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